Initial version of WRT Debugger.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/.classpath Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/.options Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,3 @@
+org.chromium.debug.core/debug=true
+org.chromium.debug.core/debug/transport=true
+org.chromium.debug.core/debug/v8DebuggerTool=true
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/.project Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.chromium.debug.core</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/LICENSE Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 The Chromium Authors. 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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/META-INF/MANIFEST.MF Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,23 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.chromium.debug.core;singleton:=true
+Bundle-Version: 0.1.3.qualifier
+Bundle-Activator: org.chromium.debug.core.ChromiumDebugPlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.core.filesystem;bundle-version="1.2.0",
+ org.eclipse.debug.ui;bundle-version="3.4.1",
+ org.eclipse.ui;bundle-version="3.4.1",
+ org.eclipse.jface.text;bundle-version="3.4.1",
+ org.eclipse.ui.workbench.texteditor;bundle-version="3.4.1",
+ org.chromium.sdk;bundle-version="0.1.3"
+Bundle-ActivationPolicy: lazy
+Bundle-ClassPath: bin/,
+ .
+Export-Package: org.chromium.debug.core,
+ org.chromium.debug.core.model,
+ org.chromium.debug.core.util
+Eclipse-LazyStart: true
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/ChromiumDebugPlugin.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/Messages.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptFileStore.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptFileSystem.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptStorage$CommonNode.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptStorage$FileNode$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptStorage$FileNode.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptStorage$FolderNode.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptStorage$RootNode.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptStorage.class has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/bin/org/chromium/debug/core/messages.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,5 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+ChromiumDebugPlugin_InternalError=Internal Error
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ArrayValue.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/BreakpointAdapterFactory.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/BreakpointRegistry$BreakpointEntry.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/BreakpointRegistry$BreakpointLocation.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/BreakpointRegistry$ScriptIdentifier.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/BreakpointRegistry.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ChromiumBreakpointWBAFactory$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ChromiumBreakpointWBAFactory.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ChromiumLineBreakpoint$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ChromiumLineBreakpoint.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ConnectionLoggerImpl$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ConnectionLoggerImpl$2.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ConnectionLoggerImpl$3.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ConnectionLoggerImpl$LogLifecycleListener.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ConnectionLoggerImpl.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ConsolePseudoProcess$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ConsolePseudoProcess$NullStreamMonitor.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ConsolePseudoProcess$Retransmitter.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ConsolePseudoProcess.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugElementImpl.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$2.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$3.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$4.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$5.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$6$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$6.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$7$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$7.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$8.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$DebugEventListenerImpl.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/Destructable.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/DestructingGuard.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/IChromiumDebugTarget.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/IResourceManager.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptThread.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedder$ConnectionToRemote.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedder$Listener.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedder$VmConnector.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedder.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$2$1$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$2$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$2.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$BrowserCache$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$BrowserCache.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$EmbeddingTabConnector$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$EmbeddingTabConnector$2.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$EmbeddingTabConnector.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/LineBreakpointAdapter.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/Messages.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/NamedConnectionLoggerFactory.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/ResourceManager.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/StackFrame$1ScopeObjectVariable.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/StackFrame.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/TabSelector.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/Value.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/Variable$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/model/Variable.class has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/bin/org/chromium/debug/core/model/messages.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,29 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+ChromiumTabSelectionDialog_DialogTitle=Select Tab to Debug
+ChromiumTabSelectionDialog_IdColumnName=ID
+ChromiumTabSelectionDialog_TableTitle=Available Tabs
+ChromiumTabSelectionDialog_UrlColumnName=Tab URL
+ConnectionLoggerImpl_ReceivedFromChrome=Received from Chrome:
+ConnectionLoggerImpl_SentToChrome=Sent to Chrome:
+DebugTargetImpl_BadResultWhileDisconnecting=Received bad result from browser while disconnecting
+DebugTargetImpl_CannotStartMultipleDebuggers=Cannot start more than one instance of debugger
+DebugTargetImpl_Caught=Caught
+DebugTargetImpl_FailedToStartSocketConnection=Failed to start socket transport
+DebugTargetImpl_LogExceptionFormat={0} {1} (in {2}:{3}): {4}
+DebugTargetImpl_TargetName=Chromium
+DebugTargetImpl_Uncaught=Uncaught
+JavascriptVmEmbedderFactory_TargetName0=Remote "{0}" embedding V8 {1}
+JavascriptVmEmbedderFactory_Terminated=terminated
+JavascriptVmEmbedderFactory_TerminatedWithReason=terminated: {0}
+JsLineBreakpoint_MessageMarkerFormat=Line Breakpoint: {0} [line: {1}]
+JsThread_ThreadLabelFormat=JavaScript Thread ({0}){1}
+JsThread_ThreadLabelRunning=Running
+JsThread_ThreadLabelSuspended=Suspended
+ResourceManager_UnnamedScriptName=(program)
+StackFrame_NameFormat={0} [{1}:{2}]
+StackFrame_UnknownScriptName=<unknown>
+Variable_NotScalarOrObjectFormat=Not scalar or object: {0}
+Variable_NullTypeForAVariable=null type for a variable
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/util/ChromiumDebugPluginUtil$1.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/util/ChromiumDebugPluginUtil$2.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/util/ChromiumDebugPluginUtil.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/util/JsValueStringifier$Config.class has changed
Binary file org.chromium.debug.core/bin/org/chromium/debug/core/util/JsValueStringifier.class has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/build.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,8 @@
+bin.includes = META-INF/,\
+ plugin.xml,\
+ .,\
+ LICENSE,\
+ plugin.properties
+source.. = src/
+output.. = bin/
+src.includes = LICENSE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/plugin.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,7 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+providerName = The Chromium Authors
+
+pluginName = Chromium JavaScript Remote Debugger Core
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/plugin.xml Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2009 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+-->
+
+<plugin>
+
+ <!-- Breakpoint-related extensions -->
+ <extension point="org.eclipse.core.runtime.adapters">
+ <factory
+ class="org.chromium.debug.core.model.BreakpointAdapterFactory"
+ adaptableType="org.eclipse.ui.texteditor.ITextEditor">
+ <adapter type="org.eclipse.debug.ui.actions.IToggleBreakpointsTarget" />
+ </factory>
+ </extension>
+
+ <extension point="org.eclipse.debug.core.breakpoints">
+ <breakpoint
+ class="org.chromium.debug.core.model.ChromiumLineBreakpoint"
+ name="JS Line Breakpoints"
+ markerType="org.chromium.debug.core.LineBP"
+ id="org.chromium.debug.core.lineBreakpoint"/>
+ </extension>
+
+ <!-- "id" value is relative to PLUGIN_ID -->
+ <extension
+ id="LineBP"
+ name="JS Line Breakpoint Marker"
+ point="org.eclipse.core.resources.markers">
+ <super type="org.eclipse.debug.core.lineBreakpointMarker"/>
+ <persistent value="true"/>
+ </extension>
+
+
+ <!-- An in-memory filesystem for the remote scripts -->
+ <extension point="org.eclipse.core.filesystem.filesystems">
+ <filesystem scheme="chromiumdebug">
+ <run class="org.chromium.debug.core.efs.ChromiumScriptFileSystem"/>
+ </filesystem>
+ </extension>
+</plugin>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/ChromiumDebugPlugin.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,112 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core;
+
+import java.text.MessageFormat;
+
+import org.chromium.debug.core.model.ChromiumBreakpointWBAFactory;
+import org.chromium.debug.core.model.ChromiumLineBreakpoint;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdapterManager;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.core.runtime.Status;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class that controls the plug-in life cycle.
+ */
+public class ChromiumDebugPlugin extends Plugin {
+
+ /** The plug-in ID. */
+ public static final String PLUGIN_ID = "org.chromium.debug.core"; //$NON-NLS-1$
+
+ /** The debug model ID. */
+ public static final String DEBUG_MODEL_ID = "org.chromium.debug"; //$NON-NLS-1$
+
+ /** The JavaScript line breakpoint marker. */
+ public static final String BP_MARKER = PLUGIN_ID + ".LineBP"; //$NON-NLS-1$
+
+ /** The shared instance. */
+ private static ChromiumDebugPlugin plugin;
+
+ private ChromiumBreakpointWBAFactory breakpointWorkbenchAdapterFactory;
+
+ public ChromiumDebugPlugin() {
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ IAdapterManager manager = Platform.getAdapterManager();
+ breakpointWorkbenchAdapterFactory = new ChromiumBreakpointWBAFactory();
+ manager.registerAdapters(breakpointWorkbenchAdapterFactory, ChromiumLineBreakpoint.class);
+ plugin = this;
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ IAdapterManager manager = Platform.getAdapterManager();
+ manager.unregisterAdapters(breakpointWorkbenchAdapterFactory);
+ super.stop(context);
+ }
+
+ /**
+ * @return the shared instance
+ */
+ public static ChromiumDebugPlugin getDefault() {
+ return plugin;
+ }
+
+ public static boolean isDebug() {
+ ChromiumDebugPlugin thePlugin = getDefault();
+ return thePlugin != null && thePlugin.isDebugging();
+ }
+
+ public static boolean isTransportDebug() {
+ return isDebug() &&
+ Boolean.valueOf(Platform.getDebugOption(PLUGIN_ID + "/debug/transport")); //$NON-NLS-1$
+ }
+
+ public static boolean isV8DebuggerToolDebug() {
+ return isDebug() &&
+ Boolean.valueOf(Platform.getDebugOption(PLUGIN_ID + "/debug/v8DebuggerTool")); //$NON-NLS-1$
+ }
+
+ public static void log(IStatus status) {
+ ChromiumDebugPlugin plugin = getDefault();
+ if (plugin != null) {
+ plugin.getLog().log(status);
+ } else {
+ System.err.println(status.getPlugin() + ": " + status.getMessage()); //$NON-NLS-1$
+ }
+ }
+
+ public static void log(Throwable e) {
+ if (e instanceof CoreException) {
+ log(new Status(IStatus.ERROR, PLUGIN_ID,
+ ((CoreException) e).getStatus().getSeverity(), e.getMessage(), e.getCause()));
+ } else {
+ log(new Status(IStatus.ERROR, PLUGIN_ID, Messages.ChromiumDebugPlugin_InternalError, e));
+ }
+ }
+
+ public static void logError(String message, Object... arguments) {
+ log(new Status(Status.ERROR, PLUGIN_ID, getPossiblyFormattedString(message, arguments)));
+ }
+
+ public static void logWarning(String message, Object... arguments) {
+ log(new Status(Status.WARNING, PLUGIN_ID, getPossiblyFormattedString(message, arguments)));
+ }
+
+ private static String getPossiblyFormattedString(String message, Object... arguments) {
+ return arguments.length > 0
+ ? MessageFormat.format(message, arguments)
+ : message;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/Messages.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,25 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * NLS messages for the package.
+ */
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME =
+ "org.chromium.debug.core.messages"; //$NON-NLS-1$
+
+ public static String ChromiumDebugPlugin_InternalError;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/efs/ChromiumScriptFileStore.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,106 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.efs;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+
+import org.eclipse.core.filesystem.IFileInfo;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.filesystem.provider.FileStore;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * A script file store. Delegates all operations to the ChromiumScriptStorage
+ * instance.
+ */
+public class ChromiumScriptFileStore extends FileStore {
+
+ /** The filesystem storage. */
+ private static final ChromiumScriptStorage STORAGE = ChromiumScriptStorage.getInstance();
+
+ /** The host filesystem of this resource. */
+ private final ChromiumScriptFileSystem fileSystem;
+
+ /** The resource path in the filesystem. */
+ private final IPath path;
+
+ /**
+ * Constructs a proxy for a real resource (which might not exist).
+ *
+ * @param fileSystem that stores the resource
+ * @param path of the resource
+ */
+ public ChromiumScriptFileStore(ChromiumScriptFileSystem fileSystem, IPath path) {
+ this.fileSystem = fileSystem;
+ this.path = path;
+ }
+
+ @Override
+ public String[] childNames(int options, IProgressMonitor monitor) throws CoreException {
+ return STORAGE.childNames(this.path);
+ }
+
+ @Override
+ public IFileInfo fetchInfo(int options, IProgressMonitor monitor) throws CoreException {
+ return STORAGE.fetchInfo(path, options);
+ }
+
+ @Override
+ public IFileStore getChild(String name) {
+ return fileSystem.getStore(path.append(name));
+ }
+
+ @Override
+ public String getName() {
+ if (path.isEmpty()) {
+ return "ROOT"; //$NON-NLS-1$
+ }
+ return path.lastSegment();
+ }
+
+ @Override
+ public IFileStore getParent() {
+ if (path.segmentCount() == 0) {
+ return null;
+ }
+ return new ChromiumScriptFileStore(fileSystem, path.removeLastSegments(1));
+ }
+
+ @Override
+ public InputStream openInputStream(int options, IProgressMonitor monitor) throws CoreException {
+ return STORAGE.openInputStream(path, options);
+ }
+
+ @Override
+ public OutputStream openOutputStream(int options, IProgressMonitor monitor) throws CoreException {
+ return STORAGE.openOutputStream(path, options);
+ }
+
+ @Override
+ public IFileStore mkdir(int options, IProgressMonitor monitor) throws CoreException {
+ STORAGE.mkdir(path, options);
+ return this;
+ }
+
+ @Override
+ public URI toURI() {
+ return ChromiumScriptFileSystem.getFileStoreUri(path);
+ }
+
+ @Override
+ public void delete(int options, IProgressMonitor monitor) throws CoreException {
+ STORAGE.delete(path, options);
+ }
+
+ @Override
+ public void putInfo(IFileInfo info, int options, IProgressMonitor monitor) throws CoreException {
+ STORAGE.putInfo(path, info, options);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/efs/ChromiumScriptFileSystem.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,69 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.efs;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.filesystem.provider.FileSystem;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+/**
+ * An in-memory filesystem for remote scripts.
+ * The supported URLs are {@code chromiumdebug:///resource_path}.
+ */
+public class ChromiumScriptFileSystem extends FileSystem {
+
+ /** All file URLs in this filesystem have this scheme. */
+ private static final String CHROMIUMDEBUG_SCHEME = "chromiumdebug"; //$NON-NLS-1$
+
+ @Override
+ public IFileStore getStore(URI uri) {
+ return new ChromiumScriptFileStore(this, toPath(uri));
+ }
+
+ /**
+ * Constructs a URI by a path.
+ *
+ * @param path of a filesystem resource
+ * @return a URI corresponding to the given {@code path}
+ */
+ public static URI getFileStoreUri(IPath path) {
+ try {
+ return new URI(CHROMIUMDEBUG_SCHEME, null, path.toPortableString(), null);
+ } catch (URISyntaxException e) {
+ ChromiumDebugPlugin.log(e);
+ return null;
+ }
+ }
+
+ /**
+ * Converts a chromiumdebug FS FileStore URI into a path relative to the FS root.
+ *
+ * @param uri to convert
+ * @return the path corresponding to the uri
+ */
+ static IPath toPath(URI uri) {
+ return Path.fromPortableString(uri.getPath()).setDevice(null);
+ }
+
+ /**
+ * Converts a chromiumdebug FS FileStore path into a FS URI.
+ *
+ * @param path to convert
+ * @return the URI corresponding to the given path
+ */
+ static URI toUri(IPath path) {
+ try {
+ return new URI(CHROMIUMDEBUG_SCHEME, null, path.toPortableString(), null);
+ } catch (URISyntaxException e) {
+ return null;
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/efs/ChromiumScriptStorage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,328 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.efs;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileInfo;
+import org.eclipse.core.filesystem.provider.FileInfo;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * A memory-based storage for browser scripts. All resource-related EFS
+ * operations are delegated into here.
+ */
+public class ChromiumScriptStorage {
+
+ /**
+ * The filesystem root path.
+ */
+ // This one should go before INSTANCE.
+ private static final IPath ROOT_PATH = new Path(null, ""); //$NON-NLS-1$
+
+ private static final ChromiumScriptStorage INSTANCE = new ChromiumScriptStorage();
+
+ public static ChromiumScriptStorage getInstance() {
+ return INSTANCE;
+ }
+
+ private static abstract class CommonNode {
+ final IPath path;
+
+ final FileInfo info;
+
+ final CommonNode parent;
+
+ CommonNode(IPath path, FolderNode parent, boolean isDirectory) {
+ this.path = path;
+ this.parent = parent;
+ this.info = new FileInfo(path.lastSegment());
+ this.info.setDirectory(isDirectory);
+ this.info.setExists(true);
+ if (parent != null) {
+ parent.add(this);
+ }
+ }
+
+ String getName() {
+ return info.getName();
+ }
+
+ boolean isFile() {
+ return !info.isDirectory();
+ }
+
+ }
+
+ private static class RootNode extends FolderNode {
+ RootNode() {
+ super(ROOT_PATH, null);
+ if (parent != null) {
+ throw new IllegalArgumentException("Parent must be null, was: " + parent); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ synchronized void add(CommonNode node) {
+ if (node.isFile()) {
+ throw new IllegalArgumentException("Cannot add files to the root"); //$NON-NLS-1$
+ }
+ super.add(node);
+ }
+
+ }
+
+ /**
+ * Contains other nodes.
+ */
+ private static class FolderNode extends CommonNode {
+ private final Map<String, CommonNode> children =
+ Collections.synchronizedMap(new HashMap<String, CommonNode>());
+
+ FolderNode(IPath path, FolderNode parent) {
+ super(path, parent, true);
+ }
+
+ void add(CommonNode node) {
+ children.put(node.getName(), node);
+ }
+
+ void remove(String name) {
+ // System.out.println(this.hashCode() + " removing " + name);
+ CommonNode removedNode = children.remove(name);
+ if (removedNode != null) {
+ removedNode.info.setExists(false);
+ }
+ }
+ }
+
+ private static class FileNode extends CommonNode {
+ private static final byte[] EMPTY_BYTES = new byte[0];
+
+ protected volatile byte[] contents = EMPTY_BYTES;
+
+ FileNode(IPath path, FolderNode parent) {
+ super(path, parent, false);
+ }
+
+ synchronized InputStream getInputStream() {
+ return new ByteArrayInputStream(contents);
+ }
+
+ synchronized OutputStream getOutputStream(final int options) {
+ return new ByteArrayOutputStream() {
+ @Override
+ public void close() throws IOException {
+ super.close();
+ byte[] data;
+ if ((options & EFS.APPEND) == 0) {
+ data = this.toByteArray();
+ } else {
+ byte[] outputData = this.toByteArray();
+ data = new byte[contents.length + this.size()];
+ System.arraycopy(contents, 0, data, 0, contents.length);
+ System.arraycopy(outputData, 0, data, contents.length, outputData.length);
+ }
+ setFileContents(data);
+ }
+ };
+
+ }
+
+ protected synchronized void setFileContents(byte[] data) {
+ contents = data;
+ info.setLength(data.length);
+ info.setLastModified(System.currentTimeMillis());
+ info.setExists(true);
+ }
+
+ }
+
+ private static final String[] EMPTY_NAMES = new String[0];
+
+ private final RootNode ROOT = new RootNode();
+
+ private CommonNode find(IPath path) {
+ if (path == null) {
+ return null;
+ }
+ CommonNode currentNode = ROOT;
+ // invariant: node(path[i]) is a folder
+ for (int i = 0, length = path.segmentCount(); i < length; i++) {
+ // > 1 segments
+ if (currentNode == null || currentNode.isFile()) {
+ // currentNode is not an existing folder
+ return null;
+ }
+ // currentNode is a folder
+ currentNode = ((FolderNode) currentNode).children.get(path.segment(i));
+ }
+ return currentNode;
+ }
+
+ String[] childNames(IPath path) {
+ Map<String, CommonNode> childrenMap = childNodes(path);
+ if (childrenMap == null) {
+ return EMPTY_NAMES;
+ }
+ return childrenMap.keySet().toArray(EMPTY_NAMES);
+ }
+
+ OutputStream openOutputStream(IPath path, int options) throws CoreException {
+ CommonNode node = find(path);
+ if (node == null) { // file does not exist
+ if (path.segmentCount() > 0) {
+ CommonNode parent = find(getParentPath(path));
+ if (parent != null && !parent.isFile()) {
+ FileNode fileNode = createFile(path, parent);
+ return fileNode.getOutputStream(options);
+ } else {
+ throw newCoreException("Bad store path (no parent folder), child=" + path, null); //$NON-NLS-1$
+ }
+ } else {
+ throw newCoreException("Cannot open OutputStream for the Root", null); //$NON-NLS-1$
+ }
+ }
+ if (node.isFile()) {
+ return ((FileNode) node).getOutputStream(options);
+ } else {
+ throw newCoreException("Cannot open a directory for writing: " + path, null); //$NON-NLS-1$
+ }
+ }
+
+ void mkdir(IPath path, int options) throws CoreException {
+ CommonNode node = find(path);
+ if (node != null || path.segmentCount() == 0) { // folder exists
+ return;
+ }
+ IPath parentPath = getParentPath(path);
+ // parentPath will not be null due to the check above
+ CommonNode parentNode = find(parentPath);
+ if ((options & EFS.SHALLOW) != 0) {
+ IPath chainPath = ROOT_PATH;
+ CommonNode childNode = null;
+ parentNode = find(ROOT_PATH);
+ for (int i = 0, length = path.segmentCount(); i < length; i++) {
+ chainPath = chainPath.append(path.segment(i));
+ childNode = find(chainPath);
+ if (childNode == null) {
+ createFolder(chainPath, parentNode);
+ parentNode = childNode;
+ continue;
+ }
+ if (childNode.isFile()) {
+ throw newCoreException("File encountered in the path: " + chainPath, null); //$NON-NLS-1$
+ }
+ }
+ } else {
+ if (parentNode == null) {
+ throw newCoreException("Parent does not exist, child=" + path, null); //$NON-NLS-1$
+ }
+ // not shallow and parent exists
+ if (!parentNode.isFile()) {
+ createFolder(path, parentNode);
+ } else {
+ throw newCoreException("Parent is a file: " + parentNode.path, null); //$NON-NLS-1$
+ }
+ }
+ }
+
+ void delete(IPath path, int options) throws CoreException {
+ CommonNode parent = find(getParentPath(path));
+ if (parent == null) {
+ return;
+ }
+ if (parent.isFile()) {
+ throw newCoreException("Parent is not a directory: " + getParentPath(path), null); //$NON-NLS-1$
+ }
+ FolderNode parentFolder = (FolderNode) parent;
+ parentFolder.remove(path.lastSegment());
+ }
+
+ InputStream openInputStream(IPath path, int options) throws CoreException {
+ CommonNode node = find(path);
+ if (node == null) {
+ throw newCoreException("File not found: " + path, null); //$NON-NLS-1$
+ }
+ if (!node.isFile()) {
+ throw newCoreException("Cannot open InputStream on directory: " + path, null); //$NON-NLS-1$
+ }
+ return ((FileNode) node).getInputStream();
+ }
+
+ IFileInfo fetchInfo(IPath path, int options) {
+ CommonNode node = find(path);
+ if (node == null) {
+ FileInfo fileInfo = new FileInfo(path.lastSegment());
+ fileInfo.setExists(false);
+ return fileInfo;
+ } else {
+ return node.info;
+ }
+ }
+
+ void putInfo(IPath path, IFileInfo info, int options) throws CoreException {
+ CommonNode node = find(path);
+ if (node == null) {
+ throw newCoreException("The store does not exist: " + path, null); //$NON-NLS-1$
+ } else {
+ if ((options & EFS.SET_ATTRIBUTES) != 0) {
+ copyAttribute(info, node.info, EFS.ATTRIBUTE_ARCHIVE);
+ copyAttribute(info, node.info, EFS.ATTRIBUTE_EXECUTABLE);
+ copyAttribute(info, node.info, EFS.ATTRIBUTE_HIDDEN);
+ copyAttribute(info, node.info, EFS.ATTRIBUTE_LINK_TARGET);
+ copyAttribute(info, node.info, EFS.ATTRIBUTE_READ_ONLY);
+ }
+ if ((options & EFS.SET_LAST_MODIFIED) != 0) {
+ node.info.setLastModified(info.getLastModified());
+ }
+ }
+ }
+
+ private static void copyAttribute(IFileInfo from, IFileInfo to, int attribute) {
+ to.setAttribute(attribute, from.getAttribute(attribute));
+ }
+
+ private static CoreException newCoreException(String message, Throwable cause) {
+ return new CoreException(
+ new Status(Status.ERROR, ChromiumDebugPlugin.PLUGIN_ID, message, cause));
+ }
+
+ private static IPath getParentPath(IPath path) {
+ if (path.segmentCount() == 0) {
+ return null;
+ }
+ return path.removeLastSegments(1);
+ }
+
+ private static void createFolder(IPath path, CommonNode parentNode) {
+ new FolderNode(path, (FolderNode) parentNode);
+ }
+
+ private static FileNode createFile(IPath path, CommonNode parent) {
+ return new FileNode(path, (FolderNode) parent);
+ }
+
+ private Map<String, CommonNode> childNodes(IPath parent) {
+ CommonNode node = find(parent);
+ if (node == null || node.isFile()) {
+ return null;
+ }
+ return ((FolderNode) node).children;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/messages.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,5 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+ChromiumDebugPlugin_InternalError=Internal Error
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/ArrayValue.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,67 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedMap;
+
+import org.chromium.sdk.JsArray;
+import org.chromium.sdk.JsVariable;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.IIndexedValue;
+import org.eclipse.debug.core.model.IVariable;
+
+/**
+ * An IIndexedValue implementation for an array element range using a JsArray
+ * instance.
+ */
+public class ArrayValue extends Value implements IIndexedValue {
+
+ private final IVariable[] elements;
+
+ public ArrayValue(IChromiumDebugTarget debugTarget, JsArray array) {
+ super(debugTarget, array);
+ this.elements = createElements();
+ }
+
+ private IVariable[] createElements() {
+ SortedMap<Integer, ? extends JsVariable> elements = ((JsArray) getJsValue()).toSparseArray();
+ List<IVariable> variables = new ArrayList<IVariable>(elements.size());
+ for (JsVariable jsVar : elements.values()) {
+ variables.add(new Variable(getDebugTarget(), jsVar, false));
+ }
+ return variables.toArray(new IVariable[variables.size()]);
+ }
+
+ public int getInitialOffset() {
+ return 0;
+ }
+
+ public int getSize() throws DebugException {
+ return elements.length;
+ }
+
+ public IVariable getVariable(int offset) throws DebugException {
+ return elements[offset];
+ }
+
+ public IVariable[] getVariables(int offset, int length) throws DebugException {
+ IVariable[] result = new IVariable[length];
+ System.arraycopy(elements, offset, result, 0, length);
+ return result;
+ }
+
+ @Override
+ public IVariable[] getVariables() throws DebugException {
+ return elements;
+ }
+
+ @Override
+ public boolean hasVariables() throws DebugException {
+ return elements.length > 0;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/BreakpointAdapterFactory.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,38 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import org.chromium.debug.core.util.ChromiumDebugPluginUtil;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdapterFactory;
+import org.eclipse.debug.ui.actions.IToggleBreakpointsTarget;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Factory of LineBreakpointAdapters for browser scripts.
+ */
+public class BreakpointAdapterFactory implements IAdapterFactory {
+
+ @SuppressWarnings("unchecked")
+ public Object getAdapter(Object adaptableObject, Class adapterType) {
+ if (adaptableObject instanceof ITextEditor) {
+ ITextEditor editorPart = (ITextEditor) adaptableObject;
+ IResource resource =
+ (IResource) editorPart.getEditorInput().getAdapter(IResource.class);
+ if (resource != null) {
+ String extension = resource.getFileExtension();
+ if (extension != null && ChromiumDebugPluginUtil.CHROMIUM_EXTENSION.equals(extension)) {
+ return new LineBreakpointAdapter();
+ }
+ }
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Class[] getAdapterList() {
+ return new Class[] { IToggleBreakpointsTarget.class };
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/BreakpointRegistry.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,227 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.Script;
+
+/**
+ * A registry of existing breakpoints associated with their script locations. It
+ * is used to restore
+ */
+public class BreakpointRegistry {
+
+ /**
+ * Script identifier for a breakpoint location.
+ */
+ public static class ScriptIdentifier {
+ private final String name;
+
+ private final long id;
+
+ private final int startLine;
+
+ private final int endLine;
+
+ public static ScriptIdentifier forScript(Script script) {
+ String name = script.getName();
+ return new ScriptIdentifier(
+ name,
+ name != null ? -1 : script.getId(),
+ script.getStartLine(),
+ script.getEndLine());
+ }
+
+ private ScriptIdentifier(String name, long id, int startLine, int endLine) {
+ this.name = name;
+ this.id = id;
+ this.startLine = startLine;
+ this.endLine = endLine;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (id ^ (id >>> 32));
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + 17 * startLine + 19 * endLine;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ScriptIdentifier)) {
+ return false;
+ }
+ ScriptIdentifier that = (ScriptIdentifier) obj;
+ if (this.startLine != that.startLine || this.endLine != that.endLine) {
+ return false;
+ }
+ if (name == null) {
+ // an unnamed script, only id is known
+ return that.name == null && this.id == that.id;
+ }
+ // a named script
+ return this.name.equals(that.name);
+ }
+ }
+
+ static class BreakpointLocation {
+ private final ScriptIdentifier scriptIdentifier;
+
+ private final int line;
+
+ public BreakpointLocation(ScriptIdentifier scriptIdentifier, int line) {
+ this.scriptIdentifier = scriptIdentifier;
+ this.line = line;
+ }
+
+ public ScriptIdentifier getScriptIdentifier() {
+ return scriptIdentifier;
+ }
+
+ public int getLine() {
+ return line;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + line;
+ result = prime * result + ((scriptIdentifier == null)
+ ? 0
+ : scriptIdentifier.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof BreakpointLocation)) {
+ return false;
+ }
+ BreakpointLocation that = (BreakpointLocation) obj;
+ return (this.line == that.line && eq(this.scriptIdentifier, that.scriptIdentifier));
+ }
+ }
+
+ /**
+ * A breakpoint accompanied by its line number in the corresponding enclosing
+ * resource.
+ */
+ public static class BreakpointEntry {
+ public final Breakpoint breakpoint;
+
+ public final int line;
+
+ private BreakpointEntry(Breakpoint breakpoint, int line) {
+ this.breakpoint = breakpoint;
+ this.line = line;
+ }
+
+ boolean isWithinScriptRange(Script script) {
+ return line >= script.getStartLine() && line <= script.getEndLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * line + 17 * breakpoint.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof BreakpointEntry)) {
+ return false;
+ }
+ BreakpointEntry that = (BreakpointEntry) obj;
+ return this.line == that.line && this.breakpoint.equals(that.breakpoint);
+ }
+ }
+
+ private final Map<ScriptIdentifier, Collection<BreakpointEntry>> scriptIdToBreakpointEntries =
+ new HashMap<ScriptIdentifier, Collection<BreakpointEntry>>();
+
+ /**
+ * Adds the given line breakpoint.
+ *
+ * @param script where the breakpoint is set
+ * @param line (0-based, like in V8) in the script
+ * @param breakpoint
+ */
+ public void add(Script script, int line, Breakpoint breakpoint) {
+ ScriptIdentifier scriptId = ScriptIdentifier.forScript(script);
+ Collection<BreakpointEntry> entries = scriptIdToBreakpointEntries.get(scriptId);
+ if (entries == null) {
+ entries = new HashSet<BreakpointEntry>();
+ scriptIdToBreakpointEntries.put(scriptId, entries);
+ }
+ entries.add(new BreakpointEntry(breakpoint, line));
+ }
+
+ /**
+ * Gets breakpoint entries for the given script. An empty collection for a
+ * {@code null} script.
+ *
+ * @param script to extract the breakpoints for
+ * @return the breakpoints that fall within the given script line range
+ */
+ public Collection<? extends BreakpointEntry> getBreakpointEntries(Script script) {
+ if (script == null) {
+ return Collections.emptySet();
+ }
+ Collection<BreakpointEntry> entries =
+ scriptIdToBreakpointEntries.get(ScriptIdentifier.forScript(script));
+ if (entries == null) {
+ return Collections.emptySet();
+ }
+ Collection<BreakpointEntry> scriptBreakpoints = new LinkedList<BreakpointEntry>();
+ // Linear search should work fairly well for a reasonable number of
+ // breakpoints per script
+ for (BreakpointEntry entry : entries) {
+ if (entry.isWithinScriptRange(script)) {
+ scriptBreakpoints.add(entry);
+ }
+ }
+ return scriptBreakpoints;
+ }
+
+ /**
+ * Removes the given line breakpoint in the given script. Does nothing for a
+ * {@code null} script (which may be the case after a navigation + disconnect
+ * when the resource referenced by the breakpoint marker is absent.)
+ *
+ * @param script where the breakpoint is set
+ * @param line (0-based, like in V8) in the script
+ * @param breakpoint
+ */
+ public void remove(Script script, int line, Breakpoint breakpoint) {
+ if (script == null) {
+ return;
+ }
+ ScriptIdentifier scriptId = ScriptIdentifier.forScript(script);
+ Collection<BreakpointEntry> entries = scriptIdToBreakpointEntries.get(scriptId);
+ if (entries == null) {
+ return;
+ }
+ if (entries.size() == 1) {
+ scriptIdToBreakpointEntries.remove(scriptId);
+ } else {
+ entries.remove(new BreakpointEntry(breakpoint, line));
+ }
+ }
+
+ protected static boolean eq(Object left, Object right) {
+ return left == right || (left != null && left.equals(right));
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/ChromiumBreakpointWBAFactory.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,58 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdapterFactory;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+/**
+ * An IWorkbenchAdapter factory for ChromiumLineBreakpoints.
+ */
+public class ChromiumBreakpointWBAFactory implements IAdapterFactory {
+
+ protected static final Object[] EMPTY_CHILDREN = new Object[0];
+
+ @SuppressWarnings("unchecked")
+ public Object getAdapter(Object adaptableObject, Class adapterType) {
+ if (adapterType != IWorkbenchAdapter.class ||
+ !(adaptableObject instanceof ChromiumLineBreakpoint)) {
+ return null;
+ }
+ return new IWorkbenchAdapter() {
+
+ public Object[] getChildren(Object o) {
+ return EMPTY_CHILDREN;
+ }
+
+ public ImageDescriptor getImageDescriptor(Object object) {
+ return null;
+ }
+
+ public String getLabel(Object o) {
+ ChromiumLineBreakpoint bp = (ChromiumLineBreakpoint) o;
+ try {
+ return bp.getMarker().getAttribute(IMarker.MESSAGE).toString();
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ return null;
+ }
+
+ public Object getParent(Object o) {
+ return null;
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ public Class[] getAdapterList() {
+ return new Class[] { IWorkbenchAdapter.class };
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/ChromiumLineBreakpoint.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,124 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.sdk.Breakpoint;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.LineBreakpoint;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * JavaScript line breakpoint.
+ */
+public class ChromiumLineBreakpoint extends LineBreakpoint {
+
+ /** Ignore count */
+ private static final String IGNORE_COUNT_ATTR = ChromiumDebugPlugin.PLUGIN_ID + ".ignoreCount"; //$NON-NLS-1$
+
+ /** Condition */
+ private static final String CONDITION_ATTR = ChromiumDebugPlugin.PLUGIN_ID + ".condition"; //$NON-NLS-1$
+
+ private Breakpoint browserBreakpoint;
+
+ /**
+ * Default constructor is required for the breakpoint manager to re-create
+ * persisted breakpoints. After instantiating a breakpoint, the setMarker
+ * method is called to restore this breakpoint's attributes.
+ */
+ // FIXME(apavlov): now this does not restore the browserBreakpoint value
+ public ChromiumLineBreakpoint() {
+ }
+
+ public void setBreakpoint(Breakpoint breakpoint) {
+ this.browserBreakpoint = breakpoint;
+ }
+
+ public Breakpoint getBrowserBreakpoint() {
+ return browserBreakpoint;
+ }
+
+ /**
+ * Constructs a line breakpoint on the given resource at the given line number
+ * (line number is 1-based).
+ *
+ * @param resource file on which to set the breakpoint
+ * @param lineNumber 1-based line number of the breakpoint
+ * @throws CoreException if unable to create the breakpoint
+ */
+ public ChromiumLineBreakpoint(final IResource resource, final int lineNumber)
+ throws CoreException {
+ IWorkspaceRunnable runnable = new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ IMarker marker = resource.createMarker(ChromiumDebugPlugin.BP_MARKER);
+ setMarker(marker);
+ marker.setAttribute(IBreakpoint.ENABLED, Boolean.TRUE);
+ marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
+ marker.setAttribute(IBreakpoint.ID, getModelIdentifier());
+ marker.setAttribute(IMarker.MESSAGE, NLS.bind(
+ Messages.JsLineBreakpoint_MessageMarkerFormat, resource.getName(), lineNumber));
+ }
+ };
+ run(getMarkerRule(resource), runnable);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ try {
+ return super.isEnabled();
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ return false;
+ }
+ }
+
+ public void setIgnoreCount(int ignoreCount) {
+ setMarkerAttribute(IGNORE_COUNT_ATTR, ignoreCount);
+ }
+
+ private void setMarkerAttribute(String attributeName, Object value) {
+ try {
+ setAttribute(attributeName, value);
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ }
+
+ public int getIgnoreCount() {
+ return getMarker().getAttribute(IGNORE_COUNT_ATTR, Breakpoint.EMPTY_VALUE);
+ }
+
+ public void setCondition(String condition) throws CoreException {
+ setMarkerAttribute(CONDITION_ATTR, condition);
+ }
+
+ public String getCondition() {
+ return getMarker().getAttribute(CONDITION_ATTR, null);
+ }
+
+ public String getModelIdentifier() {
+ return ChromiumDebugPlugin.DEBUG_MODEL_ID;
+ }
+
+ public void changed() {
+ if (browserBreakpoint != null) {
+ browserBreakpoint.setCondition(getCondition());
+ browserBreakpoint.setEnabled(isEnabled());
+ browserBreakpoint.setIgnoreCount(getIgnoreCount());
+ browserBreakpoint.flush(null);
+ }
+ }
+
+ public void clear() {
+ if (browserBreakpoint != null) {
+ browserBreakpoint.clear(null);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/ConnectionLoggerImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,154 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+
+import org.chromium.sdk.ConnectionLogger;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.model.ITerminate;
+
+/**
+ * Connection logger that writes both incoming and outgoing streams into
+ * logWriter with simple annotations.
+ */
+public class ConnectionLoggerImpl implements ConnectionLogger {
+ /**
+ * Additional interface logger sends its output to.
+ */
+ public interface LogLifecycleListener {
+ /**
+ * Notifies about logging start. Before this call {@link ConnectionLoggerImpl}
+ * is considered to be simply garbage-collectible. After this call
+ * {@link ConnectionLoggerImpl} must call {@link #logClosed()}.
+ *
+ * @param connectionLogger instance of host {@link ConnectionLoggerImpl}, which is nice
+ * to have because theoretically we may receive this call before constructor of
+ * {@link ConnectionLoggerImpl} returned
+ */
+ void logStarted(ConnectionLoggerImpl connectionLogger);
+
+ /**
+ * Notifies about log stream being closed. Technically, last messages may arrive
+ * even after this. It is supposed that log representation may be closed on this call
+ * because we are not 100% accurate.
+ */
+ void logClosed();
+ }
+
+
+ public ConnectionLoggerImpl(Writer logWriter, LogLifecycleListener lifecycleListener) {
+ this.logWriter = logWriter;
+ this.lifecycleListener = lifecycleListener;
+ }
+
+ public Writer wrapWriter(final Writer streamWriter) {
+ return new Writer() {
+ @Override
+ public void close() throws IOException {
+ streamWriter.close();
+ flushLogWriter();
+ }
+ @Override
+ public void flush() throws IOException {
+ streamWriter.flush();
+ flushLogWriter();
+ }
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ streamWriter.write(cbuf, off, len);
+
+ writeToLog(cbuf, off, len, this,
+ Messages.ConnectionLoggerImpl_SentToChrome);
+ }
+ };
+ }
+ public Reader wrapReader(final Reader streamReader) {
+ return new Reader() {
+ @Override
+ public void close() throws IOException {
+ streamReader.close();
+ flushLogWriter();
+ }
+
+ @Override
+ public int read(char[] cbuf, int off, int len) throws IOException {
+ int res = streamReader.read(cbuf, off, len);
+ if (res > 0) {
+ writeToLog(cbuf, off, res, this,
+ Messages.ConnectionLoggerImpl_ReceivedFromChrome);
+ flushLogWriter();
+ }
+ return res;
+ }
+ };
+ }
+
+ public void start() {
+ lifecycleListener.logStarted(this);
+ }
+
+ public void handleEos() {
+ isClosed = true;
+ lifecycleListener.logClosed();
+ }
+
+ public ITerminate getConnectionTerminate() {
+ return connectionTerminate;
+ }
+
+ public void setConnectionCloser(ConnectionCloser connectionCloser) {
+ this.connectionCloser = connectionCloser;
+ }
+
+ private synchronized void writeToLog(char[] cbuf, int off, int len, Object source,
+ String sourceName) {
+ try {
+ if (lastSource != source) {
+ if (lastSource != null) {
+ logWriter.append('\n');
+ }
+ logWriter.append("> ").append(sourceName).append('\n'); //$NON-NLS-1$
+ lastSource = source;
+ }
+ logWriter.write(cbuf, off, len);
+ } catch (IOException e) {
+ DebugPlugin.log(e);
+ }
+ }
+ private void flushLogWriter() {
+ try {
+ logWriter.flush();
+ } catch (IOException e) {
+ DebugPlugin.log(e);
+ }
+ }
+
+ private final Writer logWriter;
+ private final LogLifecycleListener lifecycleListener;
+ private Object lastSource = null;
+ private volatile ConnectionCloser connectionCloser = null;
+ private volatile boolean isClosed = false;
+
+ private final ITerminate connectionTerminate = new ITerminate() {
+ public boolean canTerminate() {
+ return !isClosed && connectionCloser != null;
+ }
+
+ public boolean isTerminated() {
+ return isClosed;
+ }
+
+ public void terminate() {
+ ConnectionCloser connectionCloser0 = ConnectionLoggerImpl.this.connectionCloser;
+ if (connectionCloser0 == null) {
+ throw new IllegalStateException();
+ }
+ connectionCloser0.closeConnection();
+ }
+ };
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/ConsolePseudoProcess.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,260 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.IStreamListener;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.IStreamMonitor;
+import org.eclipse.debug.core.model.IStreamsProxy;
+import org.eclipse.debug.core.model.ITerminate;
+
+/**
+ * This process corresponds to a Debugger-Chrome connection and its main
+ * purpose is to expose connection log (see process console in UI).
+ */
+public class ConsolePseudoProcess extends PlatformObject implements IProcess {
+
+ private final ILaunch launch;
+ private final Retransmitter outputMonitor;
+ private final ITerminate connectionTerminate;
+ private final String name;
+ private Map<String, String> attributes = null;
+
+ private final IStreamsProxy streamsProxy = new IStreamsProxy() {
+ public IStreamMonitor getErrorStreamMonitor() {
+ return NullStreamMonitor.INSTANCE;
+ }
+ public IStreamMonitor getOutputStreamMonitor() {
+ return outputMonitor;
+ }
+ public void write(String input) {
+ // ignore
+ }
+ };
+
+ /**
+ * Constructs a ConsolePseudoProcess, adding this process to the given launch.
+ *
+ * @param launch the parent launch of this process
+ * @param name the label used for this process
+ */
+ public ConsolePseudoProcess(ILaunch launch, String name, Retransmitter retransmitter,
+ ITerminate connectionTerminate) {
+ this.launch = launch;
+ this.name = name;
+ this.outputMonitor = retransmitter;
+ outputMonitor.consolePseudoProcess = this;
+ this.connectionTerminate = connectionTerminate;
+
+ this.launch.addProcess(this);
+ fireCreationEvent();
+ }
+
+ /**
+ * @return writer which directs its contents to process console
+ */
+ public Writer getOutputWriter() {
+ return outputMonitor;
+ }
+
+ public String getLabel() {
+ return name;
+ }
+
+ public ILaunch getLaunch() {
+ return launch;
+ }
+
+ public boolean isTerminated() {
+ return connectionTerminate.isTerminated();
+ }
+
+ public void terminate() throws DebugException {
+ connectionTerminate.terminate();
+ }
+
+ public boolean canTerminate() {
+ return connectionTerminate.canTerminate();
+ }
+
+ public IStreamsProxy getStreamsProxy() {
+ return streamsProxy;
+ }
+
+ /*
+ * We do not expect intensive usage of attributes for this class. In fact, other option was to
+ * keep this method no-op.
+ */
+ public synchronized void setAttribute(String key, String value) {
+ if (attributes == null) {
+ attributes = new HashMap<String, String>(1);
+ }
+ String origVal = attributes.get(key);
+ if (origVal != null && origVal.equals(value)) {
+ return;
+ }
+
+ attributes.put(key, value);
+ fireChangeEvent();
+ }
+
+ /*
+ * We do not expect intensive usage of attributes for this class. In fact, other option was to
+ * put a stub here.
+ */
+ public synchronized String getAttribute(String key) {
+ if (attributes == null) {
+ return null;
+ }
+ return attributes.get(key);
+ }
+
+ public int getExitValue() throws DebugException {
+ if (isTerminated()) {
+ return 0;
+ }
+ throw new DebugException(new Status(IStatus.ERROR, ChromiumDebugPlugin.PLUGIN_ID,
+ "Process hasn't been terminated yet")); //$NON-NLS-1$
+ }
+
+ private void fireCreationEvent() {
+ fireEvent(new DebugEvent(this, DebugEvent.CREATE));
+ }
+
+ private void fireEvent(DebugEvent event) {
+ DebugPlugin manager = DebugPlugin.getDefault();
+ if (manager != null) {
+ manager.fireDebugEventSet(new DebugEvent[] { event });
+ }
+ }
+
+ private void fireTerminateEvent() {
+ outputMonitor.flush();
+ fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
+ }
+
+ private void fireChangeEvent() {
+ fireEvent(new DebugEvent(this, DebugEvent.CHANGE));
+ }
+
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (adapter.equals(IProcess.class)) {
+ return this;
+ }
+ if (adapter.equals(ILaunch.class)) {
+ return getLaunch();
+ }
+ if (adapter.equals(ILaunchConfiguration.class)) {
+ return getLaunch().getLaunchConfiguration();
+ }
+ return super.getAdapter(adapter);
+ }
+
+
+ private static class NullStreamMonitor implements IStreamMonitor {
+ public void addListener(IStreamListener listener) {
+ }
+ public String getContents() {
+ return null;
+ }
+ public void removeListener(IStreamListener listener) {
+ }
+ static final NullStreamMonitor INSTANCE = new NullStreamMonitor();
+ }
+
+ /**
+ * Responsible for getting text as {@link Writer} and retransmitting it
+ * as {@link IStreamMonitor} to whoever is interested.
+ * However in its initial state it only receives signal (the text) and saves it in a buffer.
+ * For {@link Retransmitter} to start giving the signal away one should
+ * call {@link #startFlushing} method.
+ */
+ public static class Retransmitter extends Writer implements IStreamMonitor {
+ private StringWriter writer = new StringWriter();
+ private boolean isFlushing = false;
+ private final List<IStreamListener> listeners = new ArrayList<IStreamListener>(2);
+ private volatile ConsolePseudoProcess consolePseudoProcess = null;
+
+ public synchronized void addListener(IStreamListener listener) {
+ listeners.add(listener);
+ }
+
+ public String getContents() {
+ return null;
+ }
+
+ public synchronized void removeListener(IStreamListener listener) {
+ listeners.remove(listener);
+ }
+
+ @Override
+ public synchronized void flush() {
+ if (!isFlushing) {
+ return;
+ }
+ String text = writer.toString();
+ int lastLinePos;
+ final boolean flushOnlyFullLines = true;
+ if (flushOnlyFullLines) {
+ int pos = text.lastIndexOf('\n');
+ if (pos == -1) {
+ // No full line in the buffer.
+ return;
+ }
+ lastLinePos = pos + 1;
+ } else {
+ lastLinePos = text.length();
+ }
+ String readyText = text.substring(0, lastLinePos);
+ writer = new StringWriter();
+ if (lastLinePos != text.length()) {
+ String rest = text.substring(lastLinePos);
+ writer.append(rest);
+ }
+ for (IStreamListener listener : listeners) {
+ listener.streamAppended(readyText, this);
+ }
+ }
+
+ @Override
+ public synchronized void close() {
+ // do nothing
+ }
+
+ @Override
+ public synchronized void write(char[] cbuf, int off, int len) {
+ writer.write(cbuf, off, len);
+ }
+
+ public synchronized void startFlushing() {
+ isFlushing = true;
+ flush();
+ }
+
+ public void processClosed() {
+ ConsolePseudoProcess consolePseudoProcess0 = this.consolePseudoProcess;
+ if (consolePseudoProcess0 != null) {
+ consolePseudoProcess0.fireTerminateEvent();
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/DebugElementImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,44 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IDebugElement;
+
+/**
+ * A generic IDebugElement implementation.
+ */
+public class DebugElementImpl extends PlatformObject implements IDebugElement {
+
+ private final IChromiumDebugTarget debugTarget;
+
+ public DebugElementImpl(IChromiumDebugTarget debugTarget) {
+ this.debugTarget = debugTarget;
+ }
+
+ public IChromiumDebugTarget getDebugTarget() {
+ return debugTarget;
+ }
+
+ public ILaunch getLaunch() {
+ return getDebugTarget().getLaunch();
+ }
+
+ public String getModelIdentifier() {
+ return ChromiumDebugPlugin.DEBUG_MODEL_ID;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object getAdapter(Class adapter) {
+ if (adapter == IDebugElement.class) {
+ return this;
+ }
+ return super.getAdapter(adapter);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/DebugTargetImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,657 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import java.util.Collection;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.debug.core.util.ChromiumDebugPluginUtil;
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.CallFrame;
+import org.chromium.sdk.DebugContext;
+import org.chromium.sdk.DebugEventListener;
+import org.chromium.sdk.ExceptionData;
+import org.chromium.sdk.JavascriptVm;
+import org.chromium.sdk.Script;
+import org.chromium.sdk.DebugContext.State;
+import org.chromium.sdk.DebugContext.StepAction;
+import org.chromium.sdk.JavascriptVm.BreakpointCallback;
+import org.chromium.sdk.JavascriptVm.ScriptsCallback;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarkerDelta;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IBreakpointManager;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchListener;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.IMemoryBlock;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.ISourceLocator;
+import org.eclipse.debug.core.model.IStackFrame;
+import org.eclipse.debug.core.model.IThread;
+
+/**
+ * An IDebugTarget implementation for remote JavaScript debugging.
+ * Can debug any target that supports the ChromeDevTools protocol.
+ */
+public class DebugTargetImpl extends DebugElementImpl implements IChromiumDebugTarget {
+
+ private static final IThread[] EMPTY_THREADS = new IThread[0];
+
+ private static final long OPERATION_TIMEOUT_MS = 15000L;
+
+ private final ILaunch launch;
+
+ private final JavascriptThread[] threads;
+
+ private JavascriptVmEmbedder vmEmbedder = STUB_VM_EMBEDDER;
+
+ private ResourceManager resourceManager;
+
+ private BreakpointRegistry breakpointRegistry;
+
+ private IProject debugProject = null;
+
+ private DebugContext debugContext;
+
+ private boolean isSuspended = false;
+
+ private boolean isDisconnected = false;
+
+
+ public DebugTargetImpl(ILaunch launch) {
+ super(null);
+ this.launch = launch;
+ this.threads = new JavascriptThread[] { new JavascriptThread(this) };
+ }
+
+
+ /**
+ * Loads browser tabs, consults the {@code selector} which of the tabs to
+ * attach to, and if any has been selected, requests an attachment to the tab.
+ *
+ * @param projectNameBase to create for the browser scripts
+ * @param remoteServer embedding application we are connected with
+ * @param attachCallback to invoke on successful attachment
+ * @param monitor to report the progress to
+ * @return whether the target has attached to a tab
+ * @throws CoreException
+ */
+ public boolean attach(String projectNameBase,
+ JavascriptVmEmbedder.ConnectionToRemote remoteServer, DestructingGuard destructingGuard,
+ Runnable attachCallback, IProgressMonitor monitor) throws CoreException {
+ monitor.beginTask("", 2); //$NON-NLS-1$
+ JavascriptVmEmbedder.VmConnector connector = remoteServer.selectVm();
+ if (connector == null) {
+ return false;
+ }
+ monitor.worked(1);
+ return performAttach(projectNameBase, connector, destructingGuard, attachCallback);
+ }
+
+ private boolean performAttach(String projectNameBase, JavascriptVmEmbedder.VmConnector connector,
+ DestructingGuard destructingGuard, Runnable attachCallback) throws CoreException {
+ final JavascriptVmEmbedder embedder = connector.attach(embedderListener, debugEventListener);
+
+ Destructable embedderDestructor = new Destructable() {
+ public void destruct() {
+ embedder.getJavascriptVm().detach();
+ }
+ };
+
+ destructingGuard.addValue(embedderDestructor);
+
+ vmEmbedder = embedder;
+
+ // We might want to add some url-specific suffix here
+ String projectName = projectNameBase;
+ // We'd like to know when launch is removed to remove our project.
+ DebugPlugin.getDefault().getLaunchManager().addLaunchListener(launchListener);
+ this.debugProject = ChromiumDebugPluginUtil.createEmptyProject(projectName);
+ this.breakpointRegistry = new BreakpointRegistry();
+ this.resourceManager = new ResourceManager(debugProject, breakpointRegistry);
+ onAttach(projectName, attachCallback);
+ return true;
+ }
+
+ private void onAttach(String projectName, Runnable attachCallback) {
+ DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(this);
+ reloadScriptsAndPossiblyResume(attachCallback);
+ }
+
+ private void reloadScriptsAndPossiblyResume(final Runnable attachCallback) {
+ reloadScripts(true, new Runnable() {
+ public void run() {
+ try {
+ if (attachCallback != null) {
+ attachCallback.run();
+ }
+ } finally {
+ fireCreationEvent();
+ }
+ Job job = new Job("Update debugger state") {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ debugEventListener.resumedByDefault();
+ return Status.OK_STATUS;
+ }
+ };
+ job.schedule();
+ }
+ });
+ }
+
+ private void reloadScripts(boolean isSync, final Runnable runnable) {
+ Runnable command = new Runnable() {
+ public void run() {
+ vmEmbedder.getJavascriptVm().getScripts(new ScriptsCallback() {
+ public void failure(String errorMessage) {
+ ChromiumDebugPlugin.logError(errorMessage);
+ }
+
+ public void success(Collection<Script> scripts) {
+ if (!vmEmbedder.getJavascriptVm().isAttached()) {
+ return;
+ }
+ for (Script script : scripts) {
+ getResourceManager().addScript(script);
+ }
+ if (runnable != null) {
+ runnable.run();
+ }
+ }
+
+ });
+ }
+ };
+ if (isSync) {
+ command.run();
+ return;
+ }
+ Thread t = new Thread(command);
+ t.setDaemon(true);
+ t.start();
+ try {
+ t.join(OPERATION_TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ }
+
+ public String getName() throws DebugException {
+ if (vmEmbedder == null) {
+ return ""; //$NON-NLS-1$
+ }
+ return vmEmbedder.getTargetName();
+ }
+
+ public IProcess getProcess() {
+ return null;
+ }
+
+ public JavascriptVmEmbedder getJavascriptEmbedder() {
+ return vmEmbedder;
+ }
+
+ public IThread[] getThreads() throws DebugException {
+ return isDisconnected()
+ ? EMPTY_THREADS
+ : threads;
+ }
+
+ public boolean hasThreads() throws DebugException {
+ return getThreads().length > 0;
+ }
+
+ public boolean supportsBreakpoint(IBreakpoint breakpoint) {
+ return ChromiumDebugPlugin.DEBUG_MODEL_ID.equals(breakpoint.getModelIdentifier()) &&
+ !isDisconnected();
+ }
+
+ @Override
+ public DebugTargetImpl getDebugTarget() {
+ return this;
+ }
+
+ @Override
+ public ILaunch getLaunch() {
+ return launch;
+ }
+
+ @Override
+ public String getModelIdentifier() {
+ return ChromiumDebugPlugin.DEBUG_MODEL_ID;
+ }
+
+ public boolean canTerminate() {
+ return !isTerminated();
+ }
+
+ public boolean isTerminated() {
+ return isDisconnected();
+ }
+
+ public void terminate() throws DebugException {
+ disconnect();
+ }
+
+ public boolean canResume() {
+ return !isDisconnected() && isSuspended();
+ }
+
+ public synchronized boolean isSuspended() {
+ return isSuspended;
+ }
+
+ private synchronized void setSuspended(boolean isSuspended) {
+ this.isSuspended = isSuspended;
+ }
+
+ public void suspended(int detail) {
+ setSuspended(true);
+ getThread().reset();
+ fireSuspendEvent(detail);
+ }
+
+ public void resume() throws DebugException {
+ debugContext.continueVm(StepAction.CONTINUE, 1, null);
+ // Let's pretend Chromium does respond to the "continue" request immediately
+ resumed(DebugEvent.CLIENT_REQUEST);
+ }
+
+ public void resumed(int detail) {
+ fireResumeEvent(detail);
+ }
+
+ public boolean canSuspend() {
+ return !isDisconnected() && !isSuspended();
+ }
+
+ public void suspend() throws DebugException {
+ vmEmbedder.getJavascriptVm().suspend(null);
+ }
+
+ public boolean canDisconnect() {
+ return !isDisconnected();
+ }
+
+ public void disconnect() throws DebugException {
+ if (!canDisconnect()) {
+ return;
+ }
+ removeAllBreakpoints();
+ if (!vmEmbedder.getJavascriptVm().detach()) {
+ ChromiumDebugPlugin.logWarning(Messages.DebugTargetImpl_BadResultWhileDisconnecting);
+ }
+ // This is a duplicated call to disconnected().
+ // The primary one comes from V8DebuggerToolHandler#onDebuggerDetached
+ // but we want to make sure the target becomes disconnected even if
+ // there is a browser failure and it does not respond.
+ debugEventListener.disconnected();
+ }
+
+ public synchronized boolean isDisconnected() {
+ return isDisconnected;
+ }
+
+ public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
+ return null;
+ }
+
+ public boolean supportsStorageRetrieval() {
+ return false;
+ }
+
+ public IProject getDebugProject() {
+ return debugProject;
+ }
+
+ /**
+ * Fires a debug event
+ *
+ * @param event to be fired
+ */
+ public void fireEvent(DebugEvent event) {
+ DebugPlugin debugPlugin = DebugPlugin.getDefault();
+ if (debugPlugin != null) {
+ debugPlugin.fireDebugEventSet(new DebugEvent[] { event });
+ }
+ }
+
+ public void fireEventForThread(int kind, int detail) {
+ try {
+ IThread[] threads = getThreads();
+ if (threads.length > 0) {
+ fireEvent(new DebugEvent(threads[0], kind, detail));
+ }
+ } catch (DebugException e) {
+ // Actually, this is not thrown in our getThreads()
+ return;
+ }
+ }
+
+ public void fireCreationEvent() {
+ setDisconnected(false);
+ fireEventForThread(DebugEvent.CREATE, DebugEvent.UNSPECIFIED);
+ }
+
+ public synchronized void setDisconnected(boolean disconnected) {
+ isDisconnected = disconnected;
+ }
+
+ public void fireResumeEvent(int detail) {
+ setSuspended(false);
+ fireEventForThread(DebugEvent.RESUME, detail);
+ fireEvent(new DebugEvent(this, DebugEvent.RESUME, detail));
+ }
+
+ public void fireSuspendEvent(int detail) {
+ setSuspended(true);
+ fireEventForThread(DebugEvent.SUSPEND, detail);
+ fireEvent(new DebugEvent(this, DebugEvent.SUSPEND, detail));
+ }
+
+ public void fireTerminateEvent() {
+ // TODO(peter.rybin): from Alexander Pavlov: I think you need to fire a terminate event after
+ // this line, for consolePseudoProcess if one is not null.
+ fireEventForThread(DebugEvent.TERMINATE, DebugEvent.UNSPECIFIED);
+ fireEvent(new DebugEvent(this, DebugEvent.TERMINATE, DebugEvent.UNSPECIFIED));
+ fireEvent(new DebugEvent(getLaunch(), DebugEvent.TERMINATE, DebugEvent.UNSPECIFIED));
+ }
+
+ public void breakpointAdded(IBreakpoint breakpoint) {
+ if (!supportsBreakpoint(breakpoint)) {
+ return;
+ }
+ try {
+ if (breakpoint.isEnabled()) {
+ // Class cast is ensured by the supportsBreakpoint implementation
+ final ChromiumLineBreakpoint lineBreakpoint = (ChromiumLineBreakpoint) breakpoint;
+ IFile file = (IFile) breakpoint.getMarker().getResource();
+ if (getResourceManager().isAddingFile(file)) {
+ return; // restoring breakpoints in progress
+ }
+ final Script script = getResourceManager().getScript(file);
+ if (script == null) {
+ // Might be a script from a different debug target
+ return;
+ }
+ final int line = (lineBreakpoint.getLineNumber() - 1) + script.getStartLine();
+ BreakpointCallback callback = new BreakpointCallback() {
+ public void success(Breakpoint breakpoint) {
+ lineBreakpoint.setBreakpoint(breakpoint);
+ breakpointRegistry.add(script, line, breakpoint);
+ }
+
+ public void failure(String errorMessage) {
+ ChromiumDebugPlugin.logError(errorMessage);
+ }
+ };
+ // ILineBreakpoint lines are 1-based while V8 lines are 0-based
+ JavascriptVm javascriptVm = vmEmbedder.getJavascriptVm();
+ if (script.getName() != null) {
+ javascriptVm.setBreakpoint(Breakpoint.Type.SCRIPT_NAME,
+ script.getName(),
+ line,
+ Breakpoint.EMPTY_VALUE,
+ breakpoint.isEnabled(),
+ lineBreakpoint.getCondition(),
+ lineBreakpoint.getIgnoreCount(),
+ callback);
+ } else {
+ javascriptVm.setBreakpoint(Breakpoint.Type.SCRIPT_ID,
+ String.valueOf(script.getId()),
+ line,
+ Breakpoint.EMPTY_VALUE,
+ breakpoint.isEnabled(),
+ lineBreakpoint.getCondition(),
+ lineBreakpoint.getIgnoreCount(),
+ callback);
+ }
+ }
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ }
+
+ public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
+ if (!supportsBreakpoint(breakpoint)) {
+ return;
+ }
+ // Class cast is ensured by the supportsBreakpoint implementation
+ ((ChromiumLineBreakpoint) breakpoint).changed();
+ }
+
+ public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
+ if (!supportsBreakpoint(breakpoint)) {
+ return;
+ }
+ try {
+ if (breakpoint.isEnabled()) {
+ // Class cast is ensured by the supportsBreakpoint implementation
+ ChromiumLineBreakpoint lineBreakpoint = (ChromiumLineBreakpoint) breakpoint;
+ lineBreakpoint.clear();
+ breakpointRegistry.remove(
+ getResourceManager().getScript((IFile) breakpoint.getMarker().getResource()),
+ lineBreakpoint.getLineNumber() - 1,
+ lineBreakpoint.getBrowserBreakpoint());
+ }
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (ILaunch.class.equals(adapter)) {
+ return this.launch;
+ }
+ return super.getAdapter(adapter);
+ }
+
+ public IResourceManager getResourceManager() {
+ return resourceManager;
+ }
+
+ public JavascriptThread getThread() {
+ return isDisconnected()
+ ? null
+ : threads[0];
+ }
+
+ private static void breakpointsHit(Collection<? extends Breakpoint> breakpointsHit) {
+ if (breakpointsHit.isEmpty()) {
+ return;
+ }
+ IBreakpoint[] breakpoints =
+ DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(
+ ChromiumDebugPlugin.DEBUG_MODEL_ID);
+ for (IBreakpoint breakpoint : breakpoints) {
+ ChromiumLineBreakpoint jsBreakpoint = (ChromiumLineBreakpoint) breakpoint;
+ if (breakpointsHit.contains(jsBreakpoint.getBrowserBreakpoint())) {
+ jsBreakpoint.setIgnoreCount(-1); // reset ignore count as we've hit it
+ }
+ }
+ }
+
+ private static String trim(String text, int maxLength) {
+ if (text == null || text.length() <= maxLength) {
+ return text;
+ }
+ return text.substring(0, maxLength - 3) + "..."; //$NON-NLS-1$
+ }
+
+ public DebugContext getDebugContext() {
+ return debugContext;
+ }
+
+ public ISourceLocator getSourceLocator() {
+ return sourceLocator;
+ }
+
+ private void removeAllBreakpoints() {
+ IBreakpointManager breakpointManager = DebugPlugin.getDefault().getBreakpointManager();
+ IBreakpoint[] breakpoints =
+ breakpointManager.getBreakpoints(ChromiumDebugPlugin.DEBUG_MODEL_ID);
+ for (IBreakpoint bp : breakpoints) {
+ ChromiumLineBreakpoint clb = (ChromiumLineBreakpoint) bp;
+ if (clb.getBrowserBreakpoint() != null &&
+ clb.getBrowserBreakpoint().getId() != Breakpoint.INVALID_ID) {
+ clb.getBrowserBreakpoint().clear(null);
+ }
+ }
+ try {
+ breakpointManager.removeBreakpoints(breakpoints, true);
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ }
+
+ private final DebugEventListenerImpl debugEventListener = new DebugEventListenerImpl();
+
+ class DebugEventListenerImpl implements DebugEventListener {
+ // Synchronizes calls from ReaderThread of Connection and one call from some worker thread
+ private final Object suspendResumeMonitor = new Object();
+ private boolean alreadyResumedOrSuspended = false;
+
+ public void disconnected() {
+ if (!isDisconnected()) {
+ setDisconnected(true);
+ DebugPlugin.getDefault().getBreakpointManager().removeBreakpointListener(
+ DebugTargetImpl.this);
+ fireTerminateEvent();
+ }
+ }
+
+ public void resumedByDefault() {
+ synchronized (suspendResumeMonitor) {
+ if (!alreadyResumedOrSuspended) {
+ resumed();
+ }
+ }
+ }
+
+ public void resumed() {
+ synchronized (suspendResumeMonitor) {
+ DebugTargetImpl.this.resumed(DebugEvent.CLIENT_REQUEST);
+ alreadyResumedOrSuspended = true;
+ }
+ }
+
+ public void scriptLoaded(Script newScript) {
+ getResourceManager().addScript(newScript);
+ }
+
+ public void suspended(DebugContext context) {
+ synchronized (suspendResumeMonitor) {
+ DebugTargetImpl.this.debugContext = context;
+ breakpointsHit(context.getBreakpointsHit());
+ int suspendedDetail;
+ if (context.getState() == State.EXCEPTION) {
+ logExceptionFromContext(context);
+ suspendedDetail = DebugEvent.BREAKPOINT;
+ } else {
+ if (context.getBreakpointsHit().isEmpty()) {
+ suspendedDetail = DebugEvent.STEP_END;
+ } else {
+ suspendedDetail = DebugEvent.BREAKPOINT;
+ }
+ }
+ DebugTargetImpl.this.suspended(suspendedDetail);
+
+ alreadyResumedOrSuspended = true;
+ }
+ }
+ }
+
+ private void logExceptionFromContext(DebugContext context) {
+ ExceptionData exceptionData = context.getExceptionData();
+ CallFrame topFrame = context.getCallFrames().get(0);
+ Script script = topFrame.getScript();
+ ChromiumDebugPlugin.logError(
+ Messages.DebugTargetImpl_LogExceptionFormat,
+ exceptionData.isUncaught()
+ ? Messages.DebugTargetImpl_Uncaught
+ : Messages.DebugTargetImpl_Caught,
+ exceptionData.getExceptionMessage(),
+ script != null ? script.getName() : "<unknown>", //$NON-NLS-1$
+ topFrame.getLineNumber(),
+ trim(exceptionData.getSourceText(), 80));
+ }
+
+ private final JavascriptVmEmbedder.Listener embedderListener =
+ new JavascriptVmEmbedder.Listener() {
+ public void reset() {
+ getResourceManager().clear();
+ fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.STATE));
+ }
+ public void closed() {
+ debugEventListener.disconnected();
+ }
+ };
+
+ private final ILaunchListener launchListener = new ILaunchListener() {
+ public void launchAdded(ILaunch launch) {
+ }
+ public void launchChanged(ILaunch launch) {
+ }
+ // TODO(peter.rybin): maybe have one instance of listener for all targets?
+ public void launchRemoved(ILaunch launch) {
+ if (launch != DebugTargetImpl.this.launch) {
+ return;
+ }
+ DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this);
+ if (debugProject != null) {
+ ChromiumDebugPluginUtil.deleteVirtualProjectAsync(debugProject);
+ }
+ }
+ };
+
+ private final static JavascriptVmEmbedder STUB_VM_EMBEDDER = new JavascriptVmEmbedder() {
+ public JavascriptVm getJavascriptVm() {
+ //TODO(peter.rybin): decide and redo this exception
+ throw new UnsupportedOperationException();
+ }
+
+ public String getTargetName() {
+ //TODO(peter.rybin): decide and redo this exception
+ throw new UnsupportedOperationException();
+ }
+
+ public String getThreadName() {
+ //TODO(peter.rybin): decide and redo this exception
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ /**
+ * This very simple source locator works because we provide our own source files.
+ * We'll have to try harder, once we link with resource js files.
+ */
+ private final ISourceLocator sourceLocator = new ISourceLocator() {
+ public Object getSourceElement(IStackFrame stackFrame) {
+ if (stackFrame instanceof StackFrame == false) {
+ return null;
+ }
+ StackFrame jsStackFrame = (StackFrame) stackFrame;
+
+ Script script = jsStackFrame.getCallFrame().getScript();
+ if (script == null) {
+ return null;
+ }
+
+ return resourceManager.getResource(script);
+ }
+ };
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/Destructable.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,16 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+/**
+ * An interface to destruct some object. Used from {@link DestructingGuard}.
+ */
+public interface Destructable {
+ /**
+ * Destructs object wrapped in the interface. As usual exceptions are not
+ * welcome from destruct method.
+ */
+ void destruct();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/DestructingGuard.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,46 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class that destructs unfinished objects. It is needed when Java GC is not enough.
+ * It requires to be explicitly discharged if all went OK and destruction should be cancelled.
+ * Using this class may be more convenient that try/finally in Java.
+ */
+public class DestructingGuard {
+ /**
+ * Confirms that constructing has finished OKAY and no destruction is needed from now.
+ */
+ public void discharge() {
+ discharged = true;
+ }
+
+ /**
+ * This method is supposed to be called from finally clause. It performs destructing
+ * unless {@link #discharge()} has been called.
+ */
+ public void doFinally() {
+ if (discharged) {
+ return;
+ }
+ for (int i = destructables.size() - 1; i >= 0; i--) {
+ destructables.get(i).destruct();
+ }
+ discharged = true;
+ }
+
+ /**
+ * Adds another value that should be destructed. Added values are destructed in reversed order.
+ */
+ public void addValue(Destructable destructable) {
+ this.destructables.add(destructable);
+ }
+
+ private List<Destructable> destructables = new ArrayList<Destructable>(1);
+ private boolean discharged = false;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/IChromiumDebugTarget.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,13 @@
+package org.chromium.debug.core.model;
+
+import org.chromium.sdk.DebugContext;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.debug.core.model.IDebugTarget;
+
+public interface IChromiumDebugTarget extends IDebugTarget {
+ DebugContext getDebugContext();
+ void fireResumeEvent(int detail);
+ JavascriptVmEmbedder getJavascriptEmbedder();
+ IResourceManager getResourceManager();
+ IProject getDebugProject();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/IResourceManager.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,12 @@
+package org.chromium.debug.core.model;
+
+import org.chromium.sdk.Script;
+import org.eclipse.core.resources.IFile;
+
+public interface IResourceManager {
+ IFile getResource(Script script);
+ void addScript(Script script);
+ boolean isAddingFile(IFile file);
+ Script getScript(IFile file);
+ void clear();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/JavascriptThread.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,201 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import java.util.List;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.sdk.CallFrame;
+import org.chromium.sdk.DebugContext.StepAction;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.IStackFrame;
+import org.eclipse.debug.core.model.IThread;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * This class represents the only Chromium V8 VM thread.
+ */
+public class JavascriptThread extends DebugElementImpl implements IThread, IAdaptable {
+
+ private static final StackFrame[] EMPTY_FRAMES = new StackFrame[0];
+
+ /**
+ * Breakpoints this thread is suspended at or <code>null</code> if none.
+ */
+ private IBreakpoint[] breakpoints;
+
+ /**
+ * Whether this thread is stepping. V8 does not provide information if the
+ * thread is actually stepping or it is running past the last "steppable"
+ * statement.
+ */
+ private boolean isStepping = false;
+
+ /**
+ * Cached stack
+ */
+ private StackFrame[] stackFrames;
+
+ /**
+ * Constructs a new thread for the given target
+ *
+ * @param debugTarget this thread is created for
+ */
+ public JavascriptThread(IChromiumDebugTarget debugTarget) {
+ super(debugTarget);
+ }
+
+ public StackFrame[] getStackFrames() throws DebugException {
+ if (isSuspended()) {
+ ensureStackFrames();
+ return stackFrames;
+ } else {
+ return EMPTY_FRAMES;
+ }
+ }
+
+ public void reset() {
+ this.stackFrames = null;
+ }
+
+ private void ensureStackFrames() {
+ this.stackFrames = wrapStackFrames(getDebugTarget().getDebugContext().getCallFrames());
+ }
+
+ private StackFrame[] wrapStackFrames(List<? extends CallFrame> jsFrames) {
+ StackFrame[] frames = new StackFrame[jsFrames.size()];
+ for (int i = 0, size = frames.length; i < size; ++i) {
+ frames[i] = new StackFrame(getDebugTarget(), this, jsFrames.get(i));
+ }
+ return frames;
+ }
+
+ public boolean hasStackFrames() throws DebugException {
+ return isSuspended();
+ }
+
+ public int getPriority() throws DebugException {
+ return 0;
+ }
+
+ public IStackFrame getTopStackFrame() throws DebugException {
+ IStackFrame[] frames = getStackFrames();
+ if (frames.length > 0) {
+ return frames[0];
+ }
+ return null;
+ }
+
+ public String getName() throws DebugException {
+ String url = getDebugTarget().getJavascriptEmbedder().getThreadName();
+ return NLS.bind(Messages.JsThread_ThreadLabelFormat, (isSuspended()
+ ? Messages.JsThread_ThreadLabelSuspended
+ : Messages.JsThread_ThreadLabelRunning), (url.length() > 0
+ ? (" : " + url) : "")); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public IBreakpoint[] getBreakpoints() {
+ if (breakpoints == null) {
+ return new IBreakpoint[0];
+ }
+ return breakpoints;
+ }
+
+ protected void setBreakpoints(IBreakpoint[] breakpoints) {
+ this.breakpoints = breakpoints;
+ }
+
+ public boolean canResume() {
+ return isSuspended();
+ }
+
+ public boolean canSuspend() {
+ return getDebugTarget().canSuspend();
+ }
+
+ public boolean isSuspended() {
+ return getDebugTarget().isSuspended();
+ }
+
+ public void resume() throws DebugException {
+ setStepping(false);
+ getDebugTarget().resume();
+ }
+
+ public void suspend() throws DebugException {
+ getDebugTarget().suspend();
+ }
+
+ public boolean canStepInto() {
+ return isSuspended();
+ }
+
+ public boolean canStepOver() {
+ return isSuspended();
+ }
+
+ public boolean canStepReturn() {
+ return isSuspended();
+ }
+
+ public boolean isStepping() {
+ return isStepping;
+ }
+
+ private void step(StepAction stepAction, int detail) throws DebugException {
+ setStepping(true);
+ getDebugTarget().getDebugContext().continueVm(stepAction, 1, null);
+ // The suspend event should be fired once the backtrace is ready
+ // (in BacktraceProcessor).
+ getDebugTarget().fireResumeEvent(detail);
+ }
+
+ public void stepInto() throws DebugException {
+ step(StepAction.IN, DebugEvent.STEP_INTO);
+ }
+
+ public void stepOver() throws DebugException {
+ step(StepAction.OVER, DebugEvent.STEP_OVER);
+ }
+
+ public void stepReturn() throws DebugException {
+ step(StepAction.OUT, DebugEvent.STEP_RETURN);
+ }
+
+ public boolean canTerminate() {
+ return getDebugTarget().canTerminate();
+ }
+
+ public boolean isTerminated() {
+ return getDebugTarget().isTerminated();
+ }
+
+ public void terminate() throws DebugException {
+ getDebugTarget().terminate();
+ }
+
+ /**
+ * Sets whether this thread is stepping.
+ */
+ protected void setStepping(boolean stepping) {
+ isStepping = stepping;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object getAdapter(Class adapter) {
+ if (adapter == StackFrame.class) {
+ try {
+ return getTopStackFrame();
+ } catch (DebugException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ }
+ return super.getAdapter(adapter);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/JavascriptVmEmbedder.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,72 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import org.chromium.sdk.DebugEventListener;
+import org.chromium.sdk.JavascriptVm;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Abstraction of application embedding JavaScript VM. Technically subtypes
+ * of {@code JavascriptVm} describe embedding application themselves.
+ * This interface simply holds reference to {@code JavascriptVm} and adapts
+ * various subtypes of {@code JavascriptVm} to a uniform interface
+ * suitable for {@code DebugTargetImpl}. Notably, it has polymorphous method
+ * {@code #attach(Listener, DebugEventListener)}, which {@code JavascriptVm}
+ * lacks.
+ */
+public interface JavascriptVmEmbedder {
+
+ /**
+ * First intermediate object that corresponds to already connected server.
+ * This does not refer to a particular Javascript VM though:
+ * the server may contain several VMs to choose from.
+ */
+ interface ConnectionToRemote {
+ /**
+ * This method performs selecting a particular Javascript VM. This is
+ * likely to be a user-assisted activity, so this method may block
+ * indefinitely.
+ * @return null if no VM has been chosen and we should cancel the operation
+ */
+ VmConnector selectVm() throws CoreException;
+
+ void disposeConnection();
+ }
+
+ /**
+ * Intermediate object that works as an intermediate factory
+ * for {@code JavascriptVmEmbedder}.
+ */
+ interface VmConnector {
+ JavascriptVmEmbedder attach(Listener embedderListener, DebugEventListener debugEventListener)
+ throws CoreException;
+ }
+
+ /**
+ * @return not null
+ */
+ JavascriptVm getJavascriptVm();
+
+ String getTargetName();
+
+ String getThreadName();
+
+ /**
+ * Listener that should handle embedder-specific events.
+ * TODO(peter.rybin): clean-up this interface; maybe decrease number of
+ * methods.
+ */
+ interface Listener {
+ /**
+ * State of VM has been reset. All scripts might have been changed, name of
+ * target and thread might have been changed. E.g. browser tab might have
+ * been navigated from one url to another.
+ */
+ void reset();
+
+ void closed();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/JavascriptVmEmbedderFactory.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,220 @@
+package org.chromium.debug.core.model;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.sdk.Browser;
+import org.chromium.sdk.BrowserFactory;
+import org.chromium.sdk.BrowserTab;
+import org.chromium.sdk.ConnectionLogger;
+import org.chromium.sdk.DebugEventListener;
+import org.chromium.sdk.JavascriptVm;
+import org.chromium.sdk.StandaloneVm;
+import org.chromium.sdk.TabDebugEventListener;
+import org.chromium.sdk.UnsupportedVersionException;
+import org.chromium.sdk.Browser.TabFetcher;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.Status;
+
+
+public class JavascriptVmEmbedderFactory {
+
+ private static final String LOCALHOST = "127.0.0.1"; //$NON-NLS-1$
+
+ public static JavascriptVmEmbedder.ConnectionToRemote connectToChromeDevTools(int port,
+ NamedConnectionLoggerFactory connectionLoggerFactory, final TabSelector tabSelector)
+ throws CoreException {
+
+ SocketAddress address = new InetSocketAddress(LOCALHOST, port);
+ final Browser browser = browserCache.getOrCreateBrowser(address, connectionLoggerFactory);
+
+ final TabFetcher tabFetcher;
+ try {
+ tabFetcher = browser.createTabFetcher();
+ } catch (UnsupportedVersionException e) {
+ throw newCoreException(e);
+ } catch (IOException e) {
+ throw newCoreException(e);
+ }
+
+ return new JavascriptVmEmbedder.ConnectionToRemote() {
+ public JavascriptVmEmbedder.VmConnector selectVm() throws CoreException {
+ Browser.TabConnector targetTabConnector;
+ try {
+ targetTabConnector = tabSelector.selectTab(tabFetcher);
+ } catch (IOException e) {
+ throw newCoreException("Failed to get tabs for debugging", e);
+ }
+ if (targetTabConnector == null) {
+ return null;
+ }
+
+ return new EmbeddingTabConnector(targetTabConnector);
+ }
+
+ public void disposeConnection() {
+ tabFetcher.dismiss();
+ }
+ };
+ }
+
+ private static final class EmbeddingTabConnector implements JavascriptVmEmbedder.VmConnector {
+ private final Browser.TabConnector targetTabConnector;
+
+ EmbeddingTabConnector(Browser.TabConnector targetTabConnector) {
+ this.targetTabConnector = targetTabConnector;
+ }
+
+ public JavascriptVmEmbedder attach(final JavascriptVmEmbedder.Listener embedderListener,
+ final DebugEventListener debugEventListener) throws CoreException {
+ TabDebugEventListener tabDebugEventListener = new TabDebugEventListener() {
+ public DebugEventListener getDebugEventListener() {
+ return debugEventListener;
+ }
+ public void closed() {
+ embedderListener.closed();
+ }
+ public void navigated(String newUrl) {
+ embedderListener.reset();
+ }
+ };
+ final BrowserTab browserTab;
+ try {
+ browserTab = targetTabConnector.attach(tabDebugEventListener);
+ } catch (IOException e) {
+ throw newCoreException("Failed to connect to browser tab", e);
+ }
+ return new JavascriptVmEmbedder() {
+ public JavascriptVm getJavascriptVm() {
+ return browserTab;
+ }
+
+ public String getTargetName() {
+ return Messages.DebugTargetImpl_TargetName;
+ }
+
+ public String getThreadName() {
+ return browserTab.getUrl();
+ }
+ };
+ }
+ }
+
+ public static JavascriptVmEmbedder.ConnectionToRemote connectToStandalone(int port,
+ NamedConnectionLoggerFactory connectionLoggerFactory) {
+ SocketAddress address = new InetSocketAddress(LOCALHOST, port);
+ ConnectionLogger connectionLogger =
+ connectionLoggerFactory.createLogger(address.toString());
+ final StandaloneVm standaloneVm = BrowserFactory.getInstance().createStandalone(address,
+ connectionLogger);
+
+ return new JavascriptVmEmbedder.ConnectionToRemote() {
+ public JavascriptVmEmbedder.VmConnector selectVm() {
+ return new JavascriptVmEmbedder.VmConnector() {
+ public JavascriptVmEmbedder attach(JavascriptVmEmbedder.Listener embedderListener,
+ DebugEventListener debugEventListener)
+ throws CoreException {
+ embedderListener = null;
+ try {
+ standaloneVm.attach(debugEventListener);
+ } catch (IOException e) {
+ throw newCoreException("Failed to connect to V8 VM", e);
+ } catch (UnsupportedVersionException e) {
+ throw newCoreException("Failed to connect to V8 VM", e);
+ }
+ return new JavascriptVmEmbedder() {
+ public JavascriptVm getJavascriptVm() {
+ return standaloneVm;
+ }
+ public String getTargetName() {
+ String embedderName = standaloneVm.getEmbedderName();
+ String vmVersion = standaloneVm.getVmVersion();
+ String disconnectReason = standaloneVm.getDisconnectReason();
+ String targetTitle;
+ if (embedderName == null) {
+ targetTitle = ""; //$NON-NLS-1$
+ } else {
+ targetTitle = MessageFormat.format(
+ Messages.JavascriptVmEmbedderFactory_TargetName0, embedderName, vmVersion);
+ }
+ boolean isAttached = standaloneVm.isAttached();
+ if (!isAttached) {
+ String disconnectMessage;
+ if (disconnectReason == null) {
+ disconnectMessage = Messages.JavascriptVmEmbedderFactory_Terminated;
+ } else {
+ disconnectMessage = MessageFormat.format(
+ Messages.JavascriptVmEmbedderFactory_TerminatedWithReason,
+ disconnectReason);
+ }
+ targetTitle = "<" + disconnectMessage + "> " + targetTitle;
+ }
+ return targetTitle;
+ }
+ public String getThreadName() {
+ return ""; //$NON-NLS-1$
+ }
+ };
+ }
+ };
+ }
+
+ public void disposeConnection() {
+ // Nothing to do. We do not take connection for ConnectionToRemote.
+ }
+ };
+ }
+
+ private static CoreException newCoreException(String message, Throwable cause) {
+ return new CoreException(
+ new Status(Status.ERROR, ChromiumDebugPlugin.PLUGIN_ID, message, cause));
+ }
+ private static CoreException newCoreException(Exception e) {
+ return new CoreException(
+ new Status(Status.ERROR, ChromiumDebugPlugin.PLUGIN_ID,
+ "Failed to connect to the remote browser", e));
+ }
+
+ private static final BrowserCache browserCache = new BrowserCache();
+ /**
+ * Cache of browser instances.
+ */
+ private static class BrowserCache {
+
+ /**
+ * Tries to return already created instance of Browser connected to {@code address}
+ * or create new instance.
+ * However, it creates a new instance each time that {@code ConnectionLogger} is not null
+ * (because you cannot add connection logger to existing connection).
+ * @throws CoreException if browser can't be created because of conflict with connectionLogger
+ */
+ synchronized Browser getOrCreateBrowser(final SocketAddress address,
+ final NamedConnectionLoggerFactory connectionLoggerFactory) throws CoreException {
+ Browser result = address2Browser.get(address);
+ if (result == null) {
+
+ ConnectionLogger.Factory wrappedFactory = new ConnectionLogger.Factory() {
+ public ConnectionLogger newConnectionLogger() {
+ return connectionLoggerFactory.createLogger(address.toString());
+ }
+ };
+ result = createBrowserImpl(address, wrappedFactory);
+
+ address2Browser.put(address, result);
+ }
+ return result;
+ }
+ private Browser createBrowserImpl(SocketAddress address,
+ ConnectionLogger.Factory connectionLoggerFactory) {
+ return BrowserFactory.getInstance().create(address, connectionLoggerFactory);
+ }
+
+ private final Map<SocketAddress, Browser> address2Browser =
+ new HashMap<SocketAddress, Browser>();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/LineBreakpointAdapter.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,93 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.debug.core.util.ChromiumDebugPluginUtil;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.ILineBreakpoint;
+import org.eclipse.debug.ui.actions.IToggleBreakpointsTarget;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Adapter to create breakpoints in JS files.
+ */
+public class LineBreakpointAdapter implements IToggleBreakpointsTarget {
+
+ public void toggleLineBreakpoints(IWorkbenchPart part, ISelection selection)
+ throws CoreException {
+ ITextEditor textEditor = getEditor(part);
+ if (textEditor != null) {
+ IResource resource = (IResource) textEditor.getEditorInput().getAdapter(IResource.class);
+ ITextSelection textSelection = (ITextSelection) selection;
+ int lineNumber = textSelection.getStartLine();
+ IBreakpoint[] breakpoints = DebugPlugin.getDefault().getBreakpointManager().getBreakpoints(
+ ChromiumDebugPlugin.DEBUG_MODEL_ID);
+ for (int i = 0; i < breakpoints.length; i++) {
+ IBreakpoint breakpoint = breakpoints[i];
+ if (resource.equals(breakpoint.getMarker().getResource())) {
+ if (((ILineBreakpoint) breakpoint).getLineNumber() == lineNumber + 1) {
+ // remove
+ breakpoint.delete();
+ return;
+ }
+ }
+ }
+
+ // Line numbers start with 0 in V8, with 1 in Eclipse.
+ ChromiumLineBreakpoint lineBreakpoint = new ChromiumLineBreakpoint(resource, lineNumber + 1);
+ DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(lineBreakpoint);
+ }
+ }
+
+ public boolean canToggleLineBreakpoints(IWorkbenchPart part, ISelection selection) {
+ return getEditor(part) != null;
+ }
+
+ /**
+ * Returns the editor being used to edit a PDA file, associated with the given
+ * part, or <code>null</code> if none.
+ * @param part workbench part
+ * @return the editor being used to edit a PDA file, associated with the given
+ * part, or <code>null</code> if none
+ */
+ protected ITextEditor getEditor(IWorkbenchPart part) {
+ if (part instanceof ITextEditor) {
+ ITextEditor editorPart = (ITextEditor) part;
+ IResource resource = (IResource) editorPart.getEditorInput().getAdapter(IResource.class);
+ if (resource != null &&
+ ChromiumDebugPluginUtil.CHROMIUM_EXTENSION.equals(resource.getFileExtension())) {
+ return editorPart;
+ }
+ }
+ return null;
+ }
+
+ public void toggleMethodBreakpoints(IWorkbenchPart part, ISelection selection)
+ throws CoreException {
+ // TODO(apavlov): Implement method breakpoints if feasible.
+ }
+
+ public boolean canToggleMethodBreakpoints(IWorkbenchPart part, ISelection selection) {
+ // TODO(apavlov): Implement method breakpoints if feasible.
+ return true;
+ }
+
+ public void toggleWatchpoints(IWorkbenchPart part, ISelection selection)
+ throws CoreException {
+ // TODO(apavlov): Implement watchpoints if feasible.
+ }
+
+ public boolean canToggleWatchpoints(IWorkbenchPart part, ISelection selection) {
+ // TODO(apavlov): Implement watchpoints if feasible.
+ return false;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/Messages.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,72 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * NLS messages for the package.
+ */
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME =
+ "org.chromium.debug.core.model.messages"; //$NON-NLS-1$
+
+ public static String ChromiumTabSelectionDialog_DialogTitle;
+
+ public static String ChromiumTabSelectionDialog_IdColumnName;
+
+ public static String ChromiumTabSelectionDialog_TableTitle;
+
+ public static String ChromiumTabSelectionDialog_UrlColumnName;
+
+ public static String ConnectionLoggerImpl_ReceivedFromChrome;
+
+ public static String ConnectionLoggerImpl_SentToChrome;
+
+ public static String DebugTargetImpl_BadResultWhileDisconnecting;
+
+ public static String DebugTargetImpl_CannotStartMultipleDebuggers;
+
+ public static String DebugTargetImpl_Caught;
+
+ public static String DebugTargetImpl_FailedToStartSocketConnection;
+
+ public static String DebugTargetImpl_LogExceptionFormat;
+
+ public static String DebugTargetImpl_TargetName;
+
+ public static String DebugTargetImpl_Uncaught;
+
+ public static String JavascriptVmEmbedderFactory_TargetName0;
+
+ public static String JavascriptVmEmbedderFactory_Terminated;
+
+ public static String JavascriptVmEmbedderFactory_TerminatedWithReason;
+
+ public static String JsLineBreakpoint_MessageMarkerFormat;
+
+ public static String JsThread_ThreadLabelFormat;
+
+ public static String JsThread_ThreadLabelRunning;
+
+ public static String JsThread_ThreadLabelSuspended;
+
+ public static String ResourceManager_UnnamedScriptName;
+
+ public static String StackFrame_NameFormat;
+
+ public static String StackFrame_UnknownScriptName;
+
+ public static String Variable_NotScalarOrObjectFormat;
+
+ public static String Variable_NullTypeForAVariable;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/NamedConnectionLoggerFactory.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,15 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import org.chromium.sdk.ConnectionLogger;
+
+/**
+ * The factory provides {@link ConnectionLogger} that can be used to output connection
+ * traffic, supposedly to some UI.
+ */
+public interface NamedConnectionLoggerFactory {
+ ConnectionLogger createLogger(String title);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/ResourceManager.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,134 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.debug.core.model.BreakpointRegistry.BreakpointEntry;
+import org.chromium.debug.core.model.BreakpointRegistry.ScriptIdentifier;
+import org.chromium.debug.core.util.ChromiumDebugPluginUtil;
+import org.chromium.sdk.Script;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugPlugin;
+
+/**
+ * This object handles the mapping between {@link Script}s and their corresponding resources
+ * inside Eclipse.
+ */
+public class ResourceManager implements IResourceManager {
+ private final Map<IFile, Script> resourceToScript = new HashMap<IFile, Script>();
+ private final Map<ScriptIdentifier, IFile> scriptIdToResource =
+ new HashMap<ScriptIdentifier, IFile>();
+ private final IProject debugProject;
+ private final BreakpointRegistry breakpointRegistry;
+ private Object fileBeingAdded;
+
+ public ResourceManager(IProject debugProject, BreakpointRegistry breakpointRegistry) {
+ this.debugProject = debugProject;
+ this.breakpointRegistry = breakpointRegistry;
+ }
+
+ public synchronized void putScript(Script script, IFile resource) {
+ ScriptIdentifier scriptId = ScriptIdentifier.forScript(script);
+ resourceToScript.put(resource, script);
+ scriptIdToResource.put(scriptId, resource);
+ }
+
+ public synchronized Script getScript(IFile resource) {
+ return resourceToScript.get(resource);
+ }
+
+ public synchronized IFile getResource(Script script) {
+ return scriptIdToResource.get(ScriptIdentifier.forScript(script));
+ }
+
+ public synchronized boolean scriptHasResource(Script script) {
+ return getResource(script) != null;
+ }
+
+ public synchronized void clear() {
+ deleteAllScriptFiles();
+ resourceToScript.clear();
+ scriptIdToResource.clear();
+ }
+
+ private void deleteAllScriptFiles() {
+ if (!resourceToScript.isEmpty()) {
+ try {
+ ResourcesPlugin.getWorkspace().delete(
+ resourceToScript.keySet().toArray(new IFile[resourceToScript.size()]), true, null);
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ }
+ }
+
+ public synchronized void addScript(Script script) {
+ IFile scriptFile = getResource(script);
+ if (scriptFile == null) {
+ scriptFile = ChromiumDebugPluginUtil.createFile(debugProject, getScriptResourceName(script));
+ fileBeingAdded = scriptFile;
+ try {
+ putScript(script, scriptFile);
+ writeScriptSource(script, scriptFile);
+ // Perhaps restore breakpoints for the reloaded script
+ List<ChromiumLineBreakpoint> breakpoints = new LinkedList<ChromiumLineBreakpoint>();
+ for (BreakpointEntry entry : breakpointRegistry.getBreakpointEntries(script)) {
+ ChromiumLineBreakpoint lineBreakpoint;
+ try {
+ lineBreakpoint = new ChromiumLineBreakpoint(scriptFile, entry.line + 1);
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ continue;
+ }
+ lineBreakpoint.setBreakpoint(entry.breakpoint);
+ breakpoints.add(lineBreakpoint);
+ }
+ if (!breakpoints.isEmpty()) {
+ try {
+ DebugPlugin.getDefault().getBreakpointManager().addBreakpoints(
+ breakpoints.toArray(new ChromiumLineBreakpoint[breakpoints.size()]));
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ }
+ } finally {
+ fileBeingAdded = null;
+ }
+ }
+ }
+
+ private String getScriptResourceName(Script script) {
+ String name = script.getName();
+ if (name == null) {
+ name = Messages.ResourceManager_UnnamedScriptName;
+ }
+ return name;
+ }
+
+ private static void writeScriptSource(Script script, IFile file) {
+ if (script.hasSource()) {
+ try {
+ ChromiumDebugPluginUtil.writeFile(file, script.getSource());
+ } catch (final CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ }
+ }
+
+ /**
+ * @return whether the given file is being added to the target project
+ */
+ public boolean isAddingFile(IFile file) {
+ return file.equals(fileBeingAdded);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/StackFrame.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,309 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.sdk.CallFrame;
+import org.chromium.sdk.JsArray;
+import org.chromium.sdk.JsFunction;
+import org.chromium.sdk.JsObject;
+import org.chromium.sdk.JsScope;
+import org.chromium.sdk.JsValue;
+import org.chromium.sdk.JsVariable;
+import org.chromium.sdk.Script;
+import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.IRegisterGroup;
+import org.eclipse.debug.core.model.IStackFrame;
+import org.eclipse.debug.core.model.IThread;
+import org.eclipse.debug.core.model.IVariable;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * An IStackFrame implementation over a JsStackFrame instance.
+ */
+public class StackFrame extends DebugElementImpl implements IStackFrame {
+
+ private final JavascriptThread thread;
+
+ private final CallFrame stackFrame;
+
+ private IVariable[] variables;
+
+ /**
+ * Constructs a stack frame for the given handler using the FrameMirror data
+ * from the remote V8 VM.
+ *
+ * @param debugTarget the global parent
+ * @param thread for which the stack frame is created
+ * @param stackFrame an underlying SDK stack frame
+ */
+ public StackFrame(IChromiumDebugTarget debugTarget, JavascriptThread thread, CallFrame stackFrame) {
+ super(debugTarget);
+ this.thread = thread;
+ this.stackFrame = stackFrame;
+ }
+
+ public CallFrame getCallFrame() {
+ return stackFrame;
+ }
+
+ public IThread getThread() {
+ return thread;
+ }
+
+ public IVariable[] getVariables() throws DebugException {
+ if (variables == null) {
+ try {
+ variables = wrapScopes(getDebugTarget(), stackFrame.getVariableScopes(),
+ stackFrame.getReceiverVariable());
+ } catch (RuntimeException e) {
+ // We shouldn't throw RuntimeException from here, because calling
+ // ElementContentProvider#update will forget to call update.done().
+ throw new DebugException(new Status(IStatus.ERROR, ChromiumDebugPlugin.PLUGIN_ID,
+ "Failed to read variables", e)); //$NON-NLS-1$
+ }
+ }
+ return variables;
+ }
+
+ static IVariable[] wrapVariables(
+ IChromiumDebugTarget debugTarget, Collection<? extends JsVariable> jsVars,
+ Collection <? extends JsVariable> jsInternalProperties) {
+ List<Variable> vars = new ArrayList<Variable>(jsVars.size());
+ for (JsVariable jsVar : jsVars) {
+ vars.add(new Variable(debugTarget, jsVar, false));
+ }
+ for (JsVariable jsMetaVar : jsInternalProperties) {
+ vars.add(new Variable(debugTarget, jsMetaVar, true));
+ }
+ return vars.toArray(new IVariable[vars.size()]);
+ }
+
+ static IVariable[] wrapScopes(IChromiumDebugTarget debugTarget, List<? extends JsScope> jsScopes,
+ JsVariable receiverVariable) {
+ List<Variable> vars = new ArrayList<Variable>();
+
+ for (JsScope scope : jsScopes) {
+ if (scope.getType() == JsScope.Type.GLOBAL) {
+ if (receiverVariable != null) {
+ vars.add(new Variable(debugTarget, receiverVariable, false));
+ receiverVariable = null;
+ }
+ vars.add(new Variable(debugTarget, wrapScopeAsVariable(scope), false));
+ } else {
+ for (JsVariable var : scope.getVariables()) {
+ vars.add(new Variable(debugTarget, var, false));
+ }
+ }
+ }
+ if (receiverVariable != null) {
+ vars.add(new Variable(debugTarget, receiverVariable, false));
+ }
+
+ IVariable[] result = new IVariable[vars.size()];
+ // Return in reverse order.
+ for (int i = 0; i < result.length; i++) {
+ result[result.length - i - 1] = vars.get(i);
+ }
+ return result;
+ }
+
+ private static JsVariable wrapScopeAsVariable(final JsScope jsScope) {
+ class ScopeObjectVariable implements JsVariable, JsObject {
+ public String getFullyQualifiedName() {
+ return getName();
+ }
+ public String getName() {
+ // TODO(peter.rybin): should we localize it?
+ return "<" + jsScope.getType() + ">";
+ }
+ public JsValue getValue() throws UnsupportedOperationException {
+ return this;
+ }
+ public boolean isMutable() {
+ return false;
+ }
+ public boolean isReadable() {
+ return true;
+ }
+ public void setValue(String newValue, SetValueCallback callback)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
+ public JsArray asArray() {
+ return null;
+ }
+ public JsFunction asFunction() {
+ return null;
+ }
+ public String getClassName() {
+ // TODO(peter.rybin): should we localize it?
+ return "#Scope";
+ }
+ public Collection<? extends JsVariable> getProperties() throws MethodIsBlockingException {
+ return jsScope.getVariables();
+ }
+ public Collection<? extends JsVariable> getInternalProperties()
+ throws MethodIsBlockingException {
+ return Collections.emptyList();
+ }
+ public JsVariable getProperty(String name) {
+ for (JsVariable var : getProperties()) {
+ if (var.getName().equals(name)) {
+ return var;
+ }
+ }
+ return null;
+ }
+ public JsObject asObject() {
+ return this;
+ }
+ public Type getType() {
+ return Type.TYPE_OBJECT;
+ }
+ public String getValueString() {
+ return getClassName();
+ }
+ public String getRefId() {
+ return null;
+ }
+ }
+ return new ScopeObjectVariable();
+ }
+
+ public boolean hasVariables() throws DebugException {
+ return stackFrame.getReceiverVariable() != null || stackFrame.getVariableScopes().size() > 0;
+ }
+
+ public int getLineNumber() throws DebugException {
+ // convert 0-based to 1-based
+ return stackFrame.getLineNumber() + 1;
+ }
+
+ public int getCharStart() throws DebugException {
+ return stackFrame.getCharStart();
+ }
+
+ public int getCharEnd() throws DebugException {
+ return -1;
+ }
+
+ public String getName() throws DebugException {
+ String name = stackFrame.getFunctionName();
+ Script script = stackFrame.getScript();
+ if (script == null) {
+ return Messages.StackFrame_UnknownScriptName;
+ }
+ int line = script.getStartLine() + getLineNumber();
+ if (line != -1) {
+ name = NLS.bind(Messages.StackFrame_NameFormat, new Object[] {name, script.getName(), line});
+ }
+ return name;
+ }
+
+ public IRegisterGroup[] getRegisterGroups() throws DebugException {
+ return null;
+ }
+
+ public boolean hasRegisterGroups() throws DebugException {
+ return false;
+ }
+
+ public boolean canStepInto() {
+ return getThread().canStepInto();
+ }
+
+ public boolean canStepOver() {
+ return getThread().canStepOver();
+ }
+
+ public boolean canStepReturn() {
+ return getThread().canStepReturn();
+ }
+
+ public boolean isStepping() {
+ return getThread().isStepping();
+ }
+
+ public void stepInto() throws DebugException {
+ getThread().stepInto();
+ }
+
+ public void stepOver() throws DebugException {
+ getThread().stepOver();
+ }
+
+ public void stepReturn() throws DebugException {
+ getThread().stepReturn();
+ }
+
+ public boolean canResume() {
+ return getThread().canResume();
+ }
+
+ public boolean canSuspend() {
+ return getThread().canSuspend();
+ }
+
+ public boolean isSuspended() {
+ return getThread().isSuspended();
+ }
+
+ public void resume() throws DebugException {
+ getThread().resume();
+ }
+
+ public void suspend() throws DebugException {
+ getThread().suspend();
+ }
+
+ public boolean canTerminate() {
+ return getThread().canTerminate();
+ }
+
+ public boolean isTerminated() {
+ return getThread().isTerminated();
+ }
+
+ public void terminate() throws DebugException {
+ getThread().terminate();
+ }
+
+ /**
+ * Returns the name of the source file this stack frame is associated with.
+ *
+ * @return the name of the source file this stack frame is associated with
+ */
+ String getSourceName() {
+ Script script = stackFrame.getScript();
+ if (script == null) {
+ return Messages.StackFrame_UnknownScriptName;
+ }
+ return script.getName();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof StackFrame) {
+ StackFrame other = (StackFrame) obj;
+ return other.stackFrame.equals(this.stackFrame);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return stackFrame.hashCode();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/TabSelector.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,25 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import java.io.IOException;
+
+import org.chromium.sdk.Browser;
+
+/**
+ * This interface allows clients to provide various strategies
+ * for selecting a Chromium tab to debug.
+ */
+public interface TabSelector {
+
+ /**
+ * @param tabFetcher that is used to download list of tabs; list of tabs
+ * may be reloaded if needed
+ * @return a tab to debug, or null if the launch configuration should not
+ * proceed attaching to a Chromium tab
+ * @throws IOException if tabFetcher got network problems downloading tabs
+ */
+ Browser.TabConnector selectTab(Browser.TabFetcher tabFetcher) throws IOException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/Value.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,86 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.debug.core.util.JsValueStringifier;
+import org.chromium.sdk.JsArray;
+import org.chromium.sdk.JsValue;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.IValue;
+import org.eclipse.debug.core.model.IVariable;
+
+/**
+ * A generic (non-array) implementation of IValue using a JsValue instance.
+ */
+public class Value extends DebugElementImpl implements IValue {
+
+ private static final IVariable[] EMPTY_VARIABLES = new IVariable[0];
+
+ private final JsValue value;
+
+ private IVariable[] variables;
+
+ public static Value create(IChromiumDebugTarget debugTarget, JsValue value) {
+ if (JsValue.Type.TYPE_ARRAY == value.getType()) {
+ return new ArrayValue(debugTarget, (JsArray) value);
+ }
+ return new Value(debugTarget, value);
+ }
+
+ Value(IChromiumDebugTarget debugTarget, JsValue value) {
+ super(debugTarget);
+ this.value = value;
+ }
+
+ public String getReferenceTypeName() throws DebugException {
+ return value.getType().toString();
+ }
+
+ public String getValueString() throws DebugException {
+ String valueText = JsValueStringifier.toVisibleString(value);
+ if (value.asObject() != null) {
+ String ref = value.asObject().getRefId();
+ if (ref != null) {
+ valueText = valueText + " (id=" + ref + ")";
+ }
+ }
+ return valueText;
+ }
+
+ public IVariable[] getVariables() throws DebugException {
+ try {
+ if (variables == null) {
+ if (value.asObject() != null) {
+ variables = StackFrame.wrapVariables(getDebugTarget(), value.asObject().getProperties(),
+ value.asObject().getInternalProperties());
+ } else {
+ variables = EMPTY_VARIABLES;
+ }
+ }
+ return variables;
+ } catch (RuntimeException e) {
+ // We shouldn't throw RuntimeException from here, because calling
+ // ElementContentProvider#update will forget to call update.done().
+ throw new DebugException(new Status(IStatus.ERROR, ChromiumDebugPlugin.PLUGIN_ID,
+ "Failed to read variables", e)); //$NON-NLS-1$
+ }
+ }
+
+ public boolean hasVariables() throws DebugException {
+ return value.asObject() != null;
+ }
+
+ public boolean isAllocated() throws DebugException {
+ return false;
+ }
+
+ public JsValue getJsValue() {
+ return value;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/Variable.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,111 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import org.chromium.debug.core.util.ChromiumDebugPluginUtil;
+import org.chromium.sdk.JsValue;
+import org.chromium.sdk.JsVariable;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.IValue;
+import org.eclipse.debug.core.model.IVariable;
+import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapter;
+
+/**
+ * An IVariable implementation over a JsVariable instance.
+ */
+public class Variable extends DebugElementImpl implements IVariable {
+
+ private final JsVariable variable;
+
+ /**
+ * Specifies whether this variable is internal property (__proto__ etc).
+ * TODO(peter.rybin): use it in UI.
+ */
+ private final boolean isInternalProperty;
+
+ public Variable(IChromiumDebugTarget debugTarget, JsVariable variable, boolean isInternalProperty) {
+ super(debugTarget);
+ this.variable = variable;
+ this.isInternalProperty = isInternalProperty;
+ }
+
+ public String getName() throws DebugException {
+ return variable.getName();
+ }
+
+ public String getReferenceTypeName() throws DebugException {
+ return variable.getValue().getType().toString();
+ }
+
+ public IValue getValue() throws DebugException {
+ JsValue value = variable.isReadable()
+ ? variable.getValue()
+ : null;
+ if (value == null) {
+ return null;
+ }
+ return wrapValue(value);
+ }
+
+ public boolean hasValueChanged() throws DebugException {
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (IWatchExpressionFactoryAdapter.class == adapter) {
+ return new IWatchExpressionFactoryAdapter() {
+ public String createWatchExpression(IVariable variable) throws CoreException {
+ String expression = ((Variable) variable).getJsVariable().getFullyQualifiedName();
+ if (expression == null) {
+ expression = variable.getName();
+ }
+ return expression;
+ }
+ };
+ }
+ return super.getAdapter(adapter);
+ }
+
+ public void setValue(String expression) throws DebugException {
+ variable.setValue(expression, null);
+ }
+
+ public void setValue(IValue value) throws DebugException {
+ variable.setValue(((Value) value).getJsValue().getValueString(), null);
+ }
+
+ public boolean supportsValueModification() {
+ return false; // TODO(apavlov): fix once V8 supports it
+ }
+
+ public boolean verifyValue(IValue value) throws DebugException {
+ return verifyValue(value.getValueString());
+ }
+
+ public boolean verifyValue(String expression) {
+ switch (variable.getValue().getType()) {
+ case TYPE_NUMBER:
+ return ChromiumDebugPluginUtil.isInteger(expression);
+ default:
+ return true;
+ }
+ }
+
+ public boolean verifyValue(JsValue value) {
+ return verifyValue(value.getValueString());
+ }
+
+ private IValue wrapValue(JsValue value) {
+ return Value.create(getDebugTarget(), value);
+ }
+
+ public JsVariable getJsVariable() {
+ return variable;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/messages.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,29 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+ChromiumTabSelectionDialog_DialogTitle=Select Tab to Debug
+ChromiumTabSelectionDialog_IdColumnName=ID
+ChromiumTabSelectionDialog_TableTitle=Available Tabs
+ChromiumTabSelectionDialog_UrlColumnName=Tab URL
+ConnectionLoggerImpl_ReceivedFromChrome=Received from Chrome:
+ConnectionLoggerImpl_SentToChrome=Sent to Chrome:
+DebugTargetImpl_BadResultWhileDisconnecting=Received bad result from browser while disconnecting
+DebugTargetImpl_CannotStartMultipleDebuggers=Cannot start more than one instance of debugger
+DebugTargetImpl_Caught=Caught
+DebugTargetImpl_FailedToStartSocketConnection=Failed to start socket transport
+DebugTargetImpl_LogExceptionFormat={0} {1} (in {2}:{3}): {4}
+DebugTargetImpl_TargetName=Chromium
+DebugTargetImpl_Uncaught=Uncaught
+JavascriptVmEmbedderFactory_TargetName0=Remote "{0}" embedding V8 {1}
+JavascriptVmEmbedderFactory_Terminated=terminated
+JavascriptVmEmbedderFactory_TerminatedWithReason=terminated: {0}
+JsLineBreakpoint_MessageMarkerFormat=Line Breakpoint: {0} [line: {1}]
+JsThread_ThreadLabelFormat=JavaScript Thread ({0}){1}
+JsThread_ThreadLabelRunning=Running
+JsThread_ThreadLabelSuspended=Suspended
+ResourceManager_UnnamedScriptName=(program)
+StackFrame_NameFormat={0} [{1}:{2}]
+StackFrame_UnknownScriptName=<unknown>
+Variable_NotScalarOrObjectFormat=Not scalar or object: {0}
+Variable_NullTypeForAVariable=null type for a variable
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/util/ChromiumDebugPluginUtil.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,247 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.net.URI;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.debug.core.efs.ChromiumScriptFileSystem;
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourceAttributes;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * A utility for interaction with the Eclipse workspace.
+ */
+public class ChromiumDebugPluginUtil {
+
+ public static final String CHROMIUM_EXTENSION = "chromium"; //$NON-NLS-1$
+
+ public static final String JS_DEBUG_PROJECT_NATURE = "org.chromium.debug.core.jsnature"; //$NON-NLS-1$
+
+ public static final String CHROMIUM_EXTENSION_SUFFIX = "." + CHROMIUM_EXTENSION; //$NON-NLS-1$
+
+ private static final String PROJECT_EXPLORER_ID = "org.eclipse.ui.navigator.ProjectExplorer"; //$NON-NLS-1$
+
+ /**
+ * Brings up the "Project Explorer" view in the active workbench window.
+ */
+ public static void openProjectExplorerView() {
+ Display.getDefault().asyncExec(new Runnable() {
+ public void run() {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+ if (window == null) {
+ if (workbench.getWorkbenchWindowCount() == 1) {
+ window = workbench.getWorkbenchWindows()[0];
+ }
+ }
+ if (window != null) {
+ try {
+ window.getActivePage().showView(PROJECT_EXPLORER_ID);
+ } catch (PartInitException e) {
+ // ignore
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Creates an empty workspace project with the name starting with the given projectNameBase.
+ * Created project is guaranteed to be new in EFS, but workspace may happen to
+ * alreay have project with such url (left uncleaned from previous runs). Such project
+ * silently gets deleted.
+ * @param projectNameBase project name template
+ * @return the newly created project, or {@code null} if the creation failed
+ */
+ public static IProject createEmptyProject(String projectNameBase) {
+ URI projectUri;
+ String projectName;
+ try {
+ for (int uniqueNumber = 0; ; uniqueNumber++) {
+ String projectNameTry;
+ if (uniqueNumber == 0) {
+ projectNameTry = projectNameBase;
+ } else {
+ projectNameTry = projectNameBase + " (" + uniqueNumber + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ URI projectUriTry = ChromiumScriptFileSystem.getFileStoreUri(
+ new Path(null, "/" + projectNameTry)); //$NON-NLS-1$
+ IFileStore projectStore = EFS.getStore(projectUriTry);
+ if (projectStore.fetchInfo().exists()) {
+ continue;
+ } else {
+ projectUri = projectUriTry;
+ projectName = projectNameTry;
+ break;
+ }
+ }
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ return null;
+ }
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+ IProjectDescription description =
+ ResourcesPlugin.getWorkspace().newProjectDescription(projectName);
+ description.setLocationURI(projectUri);
+ description.setNatureIds(new String[] { JS_DEBUG_PROJECT_NATURE });
+ try {
+ if (project.exists()) {
+ project.delete(true, null);
+ }
+
+ project.create(description, null);
+ project.open(null);
+
+ return project;
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Removes virtual project which was created for debug session. Does its job
+ * asynchronously.
+ */
+ public static void deleteVirtualProjectAsync(final IProject debugProject) {
+ Job job = new Job("Remove virtual project") {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ URI projectUri = debugProject.getLocationURI();
+ try {
+ IFileStore projectStore = EFS.getStore(projectUri);
+ if (projectStore.fetchInfo().exists()) {
+ projectStore.delete(EFS.NONE, null);
+ }
+ debugProject.delete(true, null);
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ return new Status(IStatus.ERROR, ChromiumDebugPlugin.PLUGIN_ID,
+ "Failed to delete virtual project");
+ }
+ return Status.OK_STATUS;
+ }
+ };
+
+ job.schedule();
+ }
+
+ /**
+ * @param projectName to check for existence
+ * @return whether the project named projectName exists.
+ */
+ public static boolean projectExists(String projectName) {
+ IWorkspace ws = ResourcesPlugin.getWorkspace();
+ IProject proj = ws.getRoot().getProject(projectName);
+ return proj.exists();
+ }
+
+ /**
+ * Creates an empty file with the given filename in the given project.
+ *
+ * @param project to create the file in
+ * @param filename the base file name to create (will be sanitized for
+ * illegal chars and, in the case of a name clash, suffixed with "(N)")
+ * @return the result of IFile.getName(), or {@code null} if the creation
+ * has failed
+ */
+ public static IFile createFile(IProject project, String filename) {
+ String patchedName = new File(filename).getName().replace('?', '_'); // simple name
+ String uniqueName = patchedName;
+
+ // TODO(apavlov): refactor this?
+ for (int i = 1; i < 1000; ++i) {
+ String filePathname = uniqueName + CHROMIUM_EXTENSION_SUFFIX;
+ IFile file = project.getFile(filePathname);
+
+ if (file.exists()) {
+ uniqueName = new StringBuilder(patchedName)
+ .append(" (") //$NON-NLS-1$
+ .append(i)
+ .append(')')
+ .toString();
+ } else {
+ try {
+ file.create(new ByteArrayInputStream("".getBytes()), false, null); //$NON-NLS-1$
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ return null;
+ }
+ return file;
+ }
+ }
+
+ // Can we have 1000 same-named files?
+ return null;
+ }
+
+ /**
+ * Writes data into a resource with the given resourceName residing in the
+ * source folder of the given project. The previous file content is lost.
+ * Temporarily resets the "read-only" file attribute if one is present.
+ *
+ * @param file to set contents for
+ * @param data to write into the file
+ * @throws CoreException
+ */
+ public static void writeFile(IFile file, String data) throws CoreException {
+ if (file != null && file.exists()) {
+ ResourceAttributes resourceAttributes = file.getResourceAttributes();
+ if (resourceAttributes.isReadOnly()) {
+ resourceAttributes.setReadOnly(false);
+ file.setResourceAttributes(resourceAttributes);
+ }
+ file.setContents(new ByteArrayInputStream(data.getBytes()), IFile.FORCE, null);
+ resourceAttributes.setReadOnly(true);
+ file.setResourceAttributes(resourceAttributes);
+ }
+ }
+
+ public static boolean isInteger(String value) {
+ try {
+ Integer.parseInt(value);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ /**
+ * The container where the script sources should be put.
+ *
+ * @param project where the launch configuration stores the scripts
+ * @return the script source container
+ */
+ public static IContainer getSourceContainer(IProject project) {
+ return project;
+ }
+
+ private ChromiumDebugPluginUtil() {
+ // not instantiable
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/util/JsValueStringifier.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,193 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.util;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.SortedMap;
+
+import org.chromium.sdk.JsArray;
+import org.chromium.sdk.JsObject;
+import org.chromium.sdk.JsValue;
+import org.chromium.sdk.JsVariable;
+import org.chromium.sdk.JsValue.Type;
+
+/**
+ * A converter of JsValues into human-readable strings used in various contexts.
+ */
+public class JsValueStringifier {
+
+ /**
+ * A configuration class
+ */
+ public static class Config {
+
+ /**
+ * The maximum length of the text to render. If the value length exceeds
+ * the limit, an ellipsis will be used to truncate the value.
+ * The default is 80 characters.
+ */
+ public int maxLength = 80;
+ }
+
+ private static final String ELLIPSIS = "..."; //$NON-NLS-1$
+
+ private static final String UNKNOWN_VALUE = "<null>"; //$NON-NLS-1$
+
+ private final Config config;
+
+
+ /**
+ * Constructs a visible string for the given {@code value} (without exposing
+ * the value structure). Encloses JavaScript string values in double quotes.
+ *
+ * @param value to build a visible string for
+ * @return {@code value.getValueString()} (enclosed in double quotes if
+ * {@code value.getType() == TYPE_STRING}), or {@code null} if
+ * {@code value==null}
+ */
+ public static String toVisibleString(JsValue value) {
+ return possiblyQuoteValueString(value);
+ }
+
+ private static String possiblyQuoteValueString(JsValue value) {
+ if (value == null) {
+ return UNKNOWN_VALUE;
+ }
+ String valueString = value.getValueString();
+ return value.getType() == JsValue.Type.TYPE_STRING
+ ? "\"" + valueString + "\"" //$NON-NLS-1$ //$NON-NLS-2$
+ : valueString;
+ }
+
+ /**
+ * Use the default config values.
+ */
+ public JsValueStringifier() {
+ this.config = new Config();
+ }
+
+ /**
+ * Use the specified {@code config} data.
+ * @param config to use when rendering values.
+ */
+ public JsValueStringifier(Config config) {
+ this.config = config;
+ }
+
+ public String render(JsValue value) {
+ if (value == null) {
+ return UNKNOWN_VALUE;
+ }
+ StringBuilder output = new StringBuilder();
+ renderInternal(value, config.maxLength, true, output);
+ return output.toString();
+ }
+
+ /**
+ * @param value to render
+ * @param maxLength the maximum length of the {@code output}
+ * @param descend whether to descend into the object contents
+ * @param output to render into
+ */
+ private void renderInternal(JsValue value, int maxLength, boolean descend, StringBuilder output) {
+ if (!descend) {
+ renderPrimitive(value, maxLength, output);
+ return;
+ }
+ Type type = value.getType();
+ // TODO(apavlov): implement good stringification of other types?
+ switch (type) {
+ case TYPE_ARRAY:
+ renderArray(value.asObject().asArray(), maxLength, output);
+ break;
+ case TYPE_OBJECT:
+ renderObject(value.asObject(), maxLength, output);
+ break;
+ default:
+ renderPrimitive(value, maxLength, output);
+ break;
+ }
+ }
+
+ private void renderPrimitive(JsValue value, int maxLength, StringBuilder output) {
+ output.append(possiblyQuoteValueString(value));
+ truncate(output, maxLength, ELLIPSIS);
+ }
+
+ private void truncate(StringBuilder valueBuilder, int maxLength, String suffix) {
+ int length = valueBuilder.length();
+ if (length > maxLength) {
+ valueBuilder.setLength(maxLength);
+ valueBuilder.replace(
+ maxLength - suffix.length(), maxLength, suffix);
+ }
+ }
+
+ private StringBuilder renderArray(JsArray value, int maxLength, StringBuilder output) {
+ output.append('[');
+ SortedMap<Integer, ? extends JsVariable> indexToElement = value.toSparseArray();
+ boolean isFirst = true;
+ int maxLengthWithoutLastBracket = maxLength - 1;
+ StringBuilder elementBuilder = new StringBuilder();
+ int entriesWritten = 0;
+ for (Map.Entry<Integer, ? extends JsVariable> entry : indexToElement.entrySet()) {
+ Integer index = entry.getKey();
+ JsVariable var = entry.getValue();
+ if (!isFirst) {
+ output.append(',');
+ } else {
+ isFirst = false;
+ }
+ elementBuilder.setLength(0);
+ elementBuilder.append(index).append('=');
+ renderInternal(var.getValue(), maxLengthWithoutLastBracket /* essentially, no limit */,
+ false, elementBuilder);
+ if (output.length() + elementBuilder.length() >= maxLengthWithoutLastBracket) {
+ // reached max length
+ appendNMore(output, indexToElement.size() - entriesWritten);
+ break;
+ } else {
+ output.append(elementBuilder.toString());
+ entriesWritten++;
+ }
+ }
+ return output.append(']');
+ }
+
+ private StringBuilder renderObject(JsObject value, int maxLength, StringBuilder output) {
+ output.append('[');
+ Collection<? extends JsVariable> properties = value.getProperties();
+ boolean isFirst = true;
+ int maxLengthWithoutLastBracket = maxLength - 1;
+ StringBuilder elementBuilder = new StringBuilder();
+ int entriesWritten = 0;
+ for (JsVariable property : properties) {
+ String name = property.getName();
+ if (!isFirst) {
+ output.append(',');
+ } else {
+ isFirst = false;
+ }
+ elementBuilder.setLength(0);
+ elementBuilder.append(name).append('=');
+ renderInternal(property.getValue(), maxLengthWithoutLastBracket /* essentially, no limit */,
+ false, elementBuilder);
+ if (output.length() + elementBuilder.length() >= maxLengthWithoutLastBracket) {
+ // reached max length
+ appendNMore(output, properties.size() - entriesWritten);
+ break;
+ } else {
+ output.append(elementBuilder.toString());
+ entriesWritten++;
+ }
+ }
+ return output.append(']');
+ }
+
+ private StringBuilder appendNMore(StringBuilder output, int n) {
+ return output.append(" +").append(n).append(ELLIPSIS); //$NON-NLS-1$
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/.classpath Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/.project Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.chromium.debug.ui</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/LICENSE Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 The Chromium Authors. 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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/META-INF/MANIFEST.MF Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,19 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.chromium.debug.ui;singleton:=true
+Bundle-Version: 0.1.3.qualifier
+Bundle-Activator: org.chromium.debug.ui.ChromiumDebugUIPlugin
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.eclipse.debug.ui;bundle-version="3.4.1",
+ org.eclipse.ui.editors;bundle-version="3.4.0",
+ org.eclipse.jface.text;bundle-version="3.4.1",
+ org.eclipse.core.variables;bundle-version="3.2.100",
+ org.eclipse.ui.ide;bundle-version="3.4.1",
+ org.chromium.debug.core;bundle-version="0.1.3",
+ org.chromium.sdk;bundle-version="0.1.3"
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/ChromiumDebugUIPlugin.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/ChromiumJavascriptDecorator.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/ChromiumTabSelectionDialog$1.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/ChromiumTabSelectionDialog$2.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/ChromiumTabSelectionDialog.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/DialogBasedTabSelector$1.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/DialogBasedTabSelector.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/JsDebugModelPresentation.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/JsEvalContextManager$1.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/JsEvalContextManager.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/JsWatchExpressionDelegate$1.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/JsWatchExpressionDelegate$2.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/JsWatchExpressionDelegate$BadWatchExpressionResult.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/JsWatchExpressionDelegate$GoodWatchExpressionResult.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/JsWatchExpressionDelegate.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/Messages.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/PluginUtil$1.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/PluginUtil.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/JsBreakpointPropertiesRulerAction$1.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/JsBreakpointPropertiesRulerAction.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/JsBreakpointPropertiesRulerActionDelegate.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/JsInspectExpression.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/JsInspectSnippetAction$1.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/JsInspectSnippetAction$2.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/JsInspectSnippetAction.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/Messages.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/OpenFunctionAction$1.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/OpenFunctionAction.class has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/messages.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,7 @@
+ExpressionEvaluator_CannotEvaluateWhenNotSuspended=JavaScript thread not suspended. Cannot perform evaluation.
+ExpressionEvaluator_ErrorEvaluatingExpression=Error evaluating expression
+ExpressionEvaluator_ErrorInspectingObject=Error inspecting object
+ExpressionEvaluator_EvaluationThreadInterrupted=Evaluation thread interrupted
+ExpressionEvaluator_SocketError=Socket error while evaluating expression
+ExpressionEvaluator_UnableToEvaluateExpression=Unable to evaluate expression
+JsBreakpointPropertiesRulerAction_ItemLabel=Breakpoint Properties...
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/EditorColors.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JavascriptUtil.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsCodeScanner$1.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsCodeScanner$KeywordRule.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsCodeScanner$WordDetector.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsCodeScanner.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsDebugTextHover$1.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsDebugTextHover.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsDocumentProvider.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsEditor.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsPartitionScanner$EmptyCommentDetector.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsPartitionScanner$EmptyCommentPredicateRule.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsPartitionScanner.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsSourceViewerConfiguration$MultilineCommentScanner.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsSourceViewerConfiguration.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/ChromiumLaunchType$ConnectionLoggerFactoryImpl.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/ChromiumLaunchType.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/ChromiumRemoteTab$1.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/ChromiumRemoteTab.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTabGroup$Chromium.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTabGroup$StandaloneV8.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTabGroup.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTypeBase$1.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTypeBase$2.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTypeBase$3.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTypeBase$4.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTypeBase$5.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTypeBase.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/Messages.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/PluginVariablesUtil.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/StandaloneV8LaunchType$1.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/StandaloneV8LaunchType.class has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/messages.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,6 @@
+ChromiumRemoteTab_ShowDebuggerNetworkCommunication=Show debugger network communication console
+ChromiumRemoteTab_PortLabel=Port:
+ChromiumRemoteTab_RemoteTabName=Remote
+ChromiumRemoteTab_InvalidPortNumberError=Invalid port number
+# note that this string is a part of the hidden filename, so translate with care
+LaunchType_LogConsoleLaunchName=chromedevtools-logger
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/bin/org/chromium/debug/ui/messages.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,9 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+ChromiumDebugUIPlugin_Error=Error
+ChromiumDebugUIPlugin_Warning=Warning
+ChromiumDebugUIPlugin_Info=Info
+JsWatchExpressionDelegate_BadStackStructureWhileEvaluating=Bad stack structure encountered while evaluating
+JsWatchExpressionDelegate_ErrorEvaluatingExpression=Error evaluating expression
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/JsLineBreakpointPage$1.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/JsLineBreakpointPage$2.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/JsLineBreakpointPage$3.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/JsLineBreakpointPage$4.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/JsLineBreakpointPage$5.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/JsLineBreakpointPage.class has changed
Binary file org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/Messages.class has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/messages.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,8 @@
+JavascriptLineBreakpointPage_BreakpointConditionErrorMessage=Breakpoint condition must not be null
+JavascriptLineBreakpointPage_EnableCondition=Enable Condition
+JavascriptLineBreakpointPage_Enabled=Enabled
+JavascriptLineBreakpointPage_IgnoreCount=Ignore Count
+JavascriptLineBreakpointPage_IgnoreCountErrorMessage=Ignore count must be a positive integer
+JsLineBreakpointPage_LineNumberLabel=Line Number:
+JsLineBreakpointPage_ResourceLabel=Resource:
+JsLineBreakpointPage_UnknownLineNumber=<unknown>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/build.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,9 @@
+bin.includes = META-INF/,\
+ plugin.xml,\
+ res/,\
+ .,\
+ LICENSE,\
+ plugin.properties
+source.. = src/
+output.. = bin/
+src.includes = LICENSE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/plugin.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,15 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+providerName = The Chromium Authors
+
+pluginName = Chromium JavaScript Remote Debugger UI
+
+chromiumLaunchName = Chromium JavaScript
+standaloneV8LaunchName = Standalone V8 VM
+consolePseudoLaunchName = Browser Connection
+
+ChromiumJavascriptDecorator.label = Chromium JavaScript Decorator
+
+OpenFunctionAction.label = Open Function
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/plugin.xml Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,232 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2009 The Chromium Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style license that can be
+ found in the LICENSE file.
+-->
+
+<plugin>
+ <extension point="org.eclipse.debug.ui.debugModelPresentations">
+ <debugModelPresentation
+ class="org.chromium.debug.ui.JsDebugModelPresentation"
+ id="org.chromium.debug">
+ </debugModelPresentation>
+ </extension>
+
+ <extension point="org.eclipse.core.variables.valueVariables">
+ <variable
+ initialValue="9222"
+ name="org.chromium.debug.ui.chromium_debug_port"
+ description="ChromeDevTools Protocol connection port">
+ </variable>
+ </extension>
+
+ <extension point="org.eclipse.debug.core.launchConfigurationTypes">
+ <launchConfigurationType
+ id="org.chromium.debug.ui.LaunchType$Chromium"
+ delegate="org.chromium.debug.ui.launcher.ChromiumLaunchType"
+ modes="debug"
+ name="%chromiumLaunchName"
+ delegateName="Debug Chromium JavaScript"
+ delegateDescription="JavaScript debugger for Chromium">
+ </launchConfigurationType>
+ <launchConfigurationType
+ id="org.chromium.debug.ui.LaunchType$StandaloneV8"
+ delegate="org.chromium.debug.ui.launcher.StandaloneV8LaunchType"
+ modes="debug"
+ name="%standaloneV8LaunchName"
+ delegateName="Debug Standalone V8 JavaScript"
+ delegateDescription="JavaScript debugger for Standalone V8">
+ </launchConfigurationType>
+ <launchConfigurationType
+ id="org.chromium.debug.ui.ConsolePseudoConfigurationType"
+ modes="org.chromium.debug.pseudotype"
+ name="%consolePseudoLaunchName">
+ </launchConfigurationType>
+ </extension>
+
+ <extension point="org.eclipse.debug.ui.launchConfigurationTypeImages">
+ <launchConfigurationTypeImage
+ id="org.chromium.debug.ui.LaunchConfigTypeImage$Chromium"
+ configTypeID="org.chromium.debug.ui.LaunchType$Chromium"
+ icon="res/chromium_16.png">
+ </launchConfigurationTypeImage>
+ <launchConfigurationTypeImage
+ id="org.chromium.debug.ui.LaunchConfigTypeImage$StandaloneV8"
+ configTypeID="org.chromium.debug.ui.LaunchType$StandaloneV8"
+ icon="res/standalone_v8_16.png">
+ </launchConfigurationTypeImage>
+ <launchConfigurationTypeImage
+ id="org.chromium.debug.ui.LaunchConfigTypeImageConsolePseudoConfiguration"
+ configTypeID="org.chromium.debug.ui.ConsolePseudoConfigurationType"
+ icon="res/chromium_16.png">
+ </launchConfigurationTypeImage>
+ </extension>
+
+ <extension point="org.eclipse.debug.ui.launchConfigurationTabGroups">
+ <launchConfigurationTabGroup
+ type="org.chromium.debug.ui.LaunchType$Chromium"
+ class="org.chromium.debug.ui.launcher.LaunchTabGroup$Chromium"
+ id="org.chromium.debug.ui.LaunchTabGroup$Chromium">
+ </launchConfigurationTabGroup>
+ <launchConfigurationTabGroup
+ type="org.chromium.debug.ui.LaunchType$StandaloneV8"
+ class="org.chromium.debug.ui.launcher.LaunchTabGroup$StandaloneV8"
+ id="org.chromium.debug.ui.LaunchTabGroup$StandaloneV8">
+ </launchConfigurationTabGroup>
+ </extension>
+
+ <extension point="org.eclipse.debug.core.watchExpressionDelegates">
+ <watchExpressionDelegate
+ debugModel="org.chromium.debug"
+ delegateClass="org.chromium.debug.ui.JsWatchExpressionDelegate"/>
+ </extension>
+
+ <extension point="org.eclipse.ui.editors">
+ <editor
+ name="JS Editor"
+ extensions="chromium"
+ default="true"
+ icon="res/chromium_16.png"
+ contributorClass="org.eclipse.ui.texteditor.BasicTextEditorActionContributor"
+ class="org.chromium.debug.ui.editors.JsEditor"
+ id="org.chromium.debug.ui.editors.JsEditor">
+ </editor>
+ </extension>
+
+ <extension point="org.eclipse.ui.editorActions">
+ <editorContribution
+ targetID="org.chromium.debug.ui.editors.JsEditor"
+ id="org.chromium.debug.ui.editors.JsEditor.editorActions">
+ <action
+ label="Not Used"
+ class="org.eclipse.debug.ui.actions.RulerToggleBreakpointActionDelegate"
+ style="push"
+ actionID="RulerDoubleClick"
+ id="org.chromium.debug.ui.editor.ruler.doubleClickBreakpointAction"/>
+ <action
+ toolbarPath="evaluationGroup"
+ id="org.chromium.debug.ui.SnippetInspect"
+ definitionId="org.chromium.debug.ui.commands.Inspect"
+ class="org.chromium.debug.ui.actions.JsInspectSnippetAction"
+ enablesFor="+"
+ label="Inspect"
+ tooltip="Inspect Result of Evaluating Selected Text">
+ <enablement>
+ <and>
+ <systemProperty
+ name="org.chromium.debug.ui.debuggerActive"
+ value="true"/>
+ <objectClass
+ name="org.eclipse.jface.text.ITextSelection"/>
+ </and>
+ </enablement>
+ </action>
+ </editorContribution>
+ </extension>
+
+ <extension point="org.eclipse.ui.contexts">
+ <context
+ name="Chromium Debug"
+ parentId="org.eclipse.ui.contexts.dialogAndWindow"
+ description="Debug Chromium JavaScript"
+ id="org.chromium.debug.ui.editors.JsEditor.context">
+ </context>
+ </extension>
+
+ <extension
+ point="org.eclipse.ui.decorators">
+ <decorator
+ label="%ChromiumJavascriptDecorator.label"
+ id="org.chromium.debug.ui.decorators.ChromiumJavaScript"
+ state="true"
+ class="org.chromium.debug.ui.ChromiumJavascriptDecorator">
+ <enablement>
+ <and>
+ <objectClass name="org.eclipse.core.resources.IFile"/>
+ <objectState name="name" value="*.chromium"/>
+ </and>
+ </enablement>
+ </decorator>
+ </extension>
+
+ <extension point="org.eclipse.ui.commands">
+ <command
+ categoryId="org.eclipse.debug.ui.category.run"
+ description="Modify breakpoint properties"
+ name="Breakpoint Properties..."
+ id="org.chromium.debug.ui.breakpoint.properties">
+ </command>
+ <command
+ categoryId="org.eclipse.debug.ui.category.run"
+ description="Inspect result of evaluating selected text"
+ id="org.chromium.debug.ui.commands.Inspect"
+ name="Inspect">
+ </command>
+ </extension>
+
+ <extension point="org.eclipse.ui.bindings">
+ <key
+ sequence="M1+M2+I"
+ contextId="org.chromium.debug.ui.editors.JsEditor.context"
+ commandId="org.chromium.debug.ui.commands.Inspect"
+ schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"/>
+ </extension>
+
+ <extension point="org.eclipse.ui.popupMenus">
+ <viewerContribution
+ targetID="org.chromium.debug.ui.editors.JsEditor.ruler"
+ id="org.chromium.debug.ui.editors.JsEditor.popupMenus">
+ <action
+ label="Toggle Breakpoint"
+ class="org.eclipse.debug.ui.actions.RulerToggleBreakpointActionDelegate"
+ menubarPath="debug"
+ id="org.chromium.debug.ui.actions.EnableDisableBreakpointAction"/>
+ <action
+ label="Breakpoint Properties..."
+ class="org.chromium.debug.ui.actions.JsBreakpointPropertiesRulerActionDelegate"
+ menubarPath="group.properties"
+ id="org.chromium.debug.ui.actions.JavaBreakpointPropertiesRulerActionDelegate">
+ </action>
+ <action
+ label="Toggle Enablement"
+ class="org.eclipse.debug.ui.actions.RulerEnableDisableBreakpointActionDelegate"
+ menubarPath="debug"
+ id="org.chromium.debug.ui.actions.EnableDisableBreakpointRulerActionDelegate">
+ </action>
+ </viewerContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.popupMenus">
+ <objectContribution
+ objectClass="org.chromium.debug.core.model.Variable"
+ id="org.chromium.debug.ui.ChromiumVariableActions">
+ <action
+ label="%OpenFunctionAction.label"
+ class="org.chromium.debug.ui.actions.OpenFunctionAction"
+ menubarPath="emptyNavigationGroup"
+ enablesFor="1"
+ id="org.chromium.debug.ui.actions.OpenFunctionAction">
+ </action>
+ </objectContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.propertyPages">
+ <page
+ name="Breakpoint Properties"
+ class="org.chromium.debug.ui.propertypages.JsLineBreakpointPage"
+ id="org.chromium.debug.ui.propertypages.LineBreakpoints">
+ <enabledWhen>
+ <or>
+ <instanceof
+ value="org.chromium.debug.core.model.ChromiumLineBreakpoint">
+ </instanceof>
+ <adapt
+ type="org.chromium.debug.core.model.ChromiumLineBreakpoint">
+ </adapt>
+ </or>
+ </enabledWhen>
+ </page>
+ </extension>
+</plugin>
Binary file org.chromium.debug.ui/res/chromium_16.png has changed
Binary file org.chromium.debug.ui/res/standalone_v8_16.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/ChromiumDebugUIPlugin.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,127 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class ChromiumDebugUIPlugin extends AbstractUIPlugin {
+
+ /** The plug-in ID. */
+ public static final String PLUGIN_ID = "org.chromium.debug.ui"; //$NON-NLS-1$
+
+ /** Editor ID for JS files. */
+ public static final String EDITOR_ID = PLUGIN_ID + ".editor"; //$NON-NLS-1$
+
+ /** The shared instance. */
+ private static ChromiumDebugUIPlugin plugin;
+
+ public ChromiumDebugUIPlugin() {
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ JsEvalContextManager.startup();
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance.
+ *
+ * @return the shared instance
+ */
+ public static ChromiumDebugUIPlugin getDefault() {
+ return plugin;
+ }
+
+ /**
+ * @return a current display, or the default one if the current one is not
+ * available
+ */
+ public static Display getDisplay() {
+ Display display = Display.getCurrent();
+ if (display == null) {
+ display = Display.getDefault();
+ }
+ return display;
+ }
+
+ /**
+ * @return the active workbench shell, or {@code null} if one is not available
+ */
+ public static Shell getActiveWorkbenchShell() {
+ IWorkbenchWindow window = getActiveWorkbenchWindow();
+ if (window != null) {
+ return window.getShell();
+ }
+ return null;
+ }
+
+ /**
+ * @return the active workbench window
+ */
+ public static IWorkbenchWindow getActiveWorkbenchWindow() {
+ return getDefault().getWorkbench().getActiveWorkbenchWindow();
+ }
+
+ /**
+ * Creates a status dialog using the given {@code status}.
+ *
+ * @param status to derive the severity
+ */
+ public static void statusDialog(IStatus status) {
+ switch (status.getSeverity()) {
+ case IStatus.ERROR:
+ statusDialog(Messages.ChromiumDebugUIPlugin_Error, status);
+ break;
+ case IStatus.WARNING:
+ statusDialog(Messages.ChromiumDebugUIPlugin_Warning, status);
+ break;
+ case IStatus.INFO:
+ statusDialog(Messages.ChromiumDebugUIPlugin_Info, status);
+ break;
+ }
+ }
+
+ /**
+ * Creates a status dialog using the given {@code status} and {@code title}.
+ *
+ * @param title of the dialog
+ * @param status to derive the severity
+ */
+ public static void statusDialog(String title, IStatus status) {
+ Shell shell = getActiveWorkbenchWindow().getShell();
+ if (shell != null) {
+ switch (status.getSeverity()) {
+ case IStatus.ERROR:
+ ErrorDialog.openError(shell, title, null, status);
+ break;
+ case IStatus.WARNING:
+ MessageDialog.openWarning(shell, title, status.getMessage());
+ break;
+ case IStatus.INFO:
+ MessageDialog.openInformation(shell, title, status.getMessage());
+ break;
+ }
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/ChromiumJavascriptDecorator.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,53 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.viewers.ILabelDecorator;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * A decorator that removes the ".chromium" file extension
+ * in the Project Explorer for the Debug Javascript project files.
+ */
+public class ChromiumJavascriptDecorator implements ILabelDecorator {
+
+ public Image decorateImage(Image image, Object element) {
+ return image;
+ }
+
+ public String decorateText(String text, Object element) {
+ // (element instanceof IFile) is guaranteed by the enablement in plugin.xml
+ return getDecoratedText(text, element);
+ }
+
+ /**
+ * @param text the original label of the element
+ * @param element must be an IFile instance
+ * @return a decorated element label, or the original one if the label
+ * need not be decorated or there was a CoreException reading
+ * the element's project natures
+ */
+ public static String getDecoratedText(String text, Object element) {
+ if (PluginUtil.isChromiumDebugFile((IFile) element)) {
+ return PluginUtil.stripChromiumExtension(text);
+ }
+ return text;
+ }
+
+ public void addListener(ILabelProviderListener listener) {
+ }
+
+ public void dispose() {
+ }
+
+ public boolean isLabelProperty(Object element, String property) {
+ return false;
+ }
+
+ public void removeListener(ILabelProviderListener listener) {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/ChromiumTabSelectionDialog.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,124 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui;
+
+import java.util.List;
+
+import org.chromium.debug.core.model.Messages;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+/**
+ * A dialog where users select which Chromium tab to attach to.
+ */
+class ChromiumTabSelectionDialog extends Dialog {
+
+ private final List<String> urls;
+
+ private Table table;
+
+ private int selectedLine = -1;
+
+ ChromiumTabSelectionDialog(Shell shell, List<String> urls) {
+ super(shell);
+ this.urls = urls;
+ }
+
+ @Override
+ protected void configureShell(Shell shell) {
+ super.configureShell(shell);
+ shell.setText(Messages.ChromiumTabSelectionDialog_DialogTitle);
+ }
+
+ @Override
+ public int open() {
+ return super.open();
+ }
+
+ @Override
+ public void create() {
+ super.create();
+ updateOkButton();
+ }
+
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite composite = (Composite) super.createDialogArea(parent);
+ Label label = new Label(composite, SWT.NONE);
+ label.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
+ label.setText(Messages.ChromiumTabSelectionDialog_TableTitle);
+
+ table = new Table(composite, SWT.VIRTUAL | SWT.BORDER | SWT.SINGLE);
+ table.setHeaderVisible(true);
+ table.setLinesVisible(true);
+ table.setItemCount(10);
+ final TableColumn urlColumn = new TableColumn(table, SWT.NONE);
+ urlColumn.setText(Messages.ChromiumTabSelectionDialog_UrlColumnName);
+ urlColumn.setWidth(400);
+
+ table.addListener(SWT.SetData, new Listener() {
+ public void handleEvent(Event event) {
+ if (table.isDisposed()) {
+ return;
+ }
+ processData(event);
+ }
+
+ private void processData(Event event) {
+ TableItem item = (TableItem) event.item;
+ int index = table.indexOf(item);
+ if (index < urls.size()) {
+ item.setText(urls.get(index));
+ GridData data = new GridData();
+ data.grabExcessHorizontalSpace = true;
+ item.setData(data);
+ if (index == 0) {
+ table.select(0);
+ }
+ }
+ }
+ });
+ table.addSelectionListener(new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ okPressed();
+ }
+ public void widgetSelected(SelectionEvent e) {
+ }
+ });
+
+ table.setItemCount(urls.size());
+ table.clearAll();
+
+ return composite;
+ }
+
+ private void updateOkButton() {
+ this.getButton(IDialogConstants.OK_ID).setEnabled(urls.size() != 0);
+ }
+
+ @Override
+ protected void okPressed() {
+ selectedLine = table.getSelectionIndex();
+ super.okPressed();
+ }
+
+ int getSelectedLine() {
+ return selectedLine;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/DialogBasedTabSelector.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,75 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.chromium.debug.core.model.TabSelector;
+import org.chromium.sdk.Browser;
+import org.chromium.sdk.Browser.TabConnector;
+import org.chromium.sdk.Browser.TabFetcher;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * A TabSelector which brings up a dialog allowing users to select which target
+ * browser tab to debug.
+ */
+public class DialogBasedTabSelector implements TabSelector {
+
+ public TabConnector selectTab(TabFetcher tabFetcher) throws IOException {
+ List<? extends Browser.TabConnector> allTabs = tabFetcher.getTabs();
+
+ List<Browser.TabConnector> filteredTabs = new ArrayList<TabConnector>(allTabs.size());
+
+ for (Browser.TabConnector tab : allTabs) {
+ if (!tab.isAlreadyAttached()) {
+ filteredTabs.add(tab);
+ }
+ }
+
+ if (autoSelectSingleTab()) {
+ if (allTabs.size() == 1 && filteredTabs.size() == 1) {
+ // if all crystal clear -- choose by default
+ // disable auto-select if there are some already attached tabs:
+ // user has already seen this dialog and might have got used to it
+ // he might not understand why it didn't show up this time
+ return allTabs.get(0);
+ }
+ }
+
+ final Map<Integer, Browser.TabConnector> map = new HashMap<Integer, Browser.TabConnector>();
+ final List<String> urls = new ArrayList<String>(filteredTabs.size());
+ for (int i = 0; i < filteredTabs.size(); ++i) {
+ Browser.TabConnector connector = filteredTabs.get(i);
+ map.put(i, connector);
+ urls.add(connector.getUrl());
+ }
+ final Browser.TabConnector[] result = { null };
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ final Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+ final ChromiumTabSelectionDialog dialog = new ChromiumTabSelectionDialog(shell, urls);
+ dialog.setBlockOnOpen(true);
+ int dialogResult = dialog.open();
+ if (dialogResult == ChromiumTabSelectionDialog.OK) {
+ result[0] = map.get(dialog.getSelectedLine());
+ }
+ // otherwise (result[0] == null) which means "Do not attach"
+ }
+ });
+ return result[0];
+ }
+
+ private boolean autoSelectSingleTab() {
+ return true;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/JsDebugModelPresentation.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,93 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui;
+
+import org.chromium.debug.core.model.Value;
+import org.chromium.debug.ui.editors.JsEditor;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.ILineBreakpoint;
+import org.eclipse.debug.core.model.IValue;
+import org.eclipse.debug.ui.IDebugModelPresentation;
+import org.eclipse.debug.ui.IValueDetailListener;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.part.FileEditorInput;
+
+/**
+ * An IDebugModelPresentation for the Chromium JavaScript debug model.
+ */
+public class JsDebugModelPresentation extends LabelProvider implements IDebugModelPresentation {
+
+ public void setAttribute(String attribute, Object value) {
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ // use default image
+ return null;
+ }
+
+ @Override
+ public String getText(Object element) {
+ // use default label text
+ return null;
+ }
+
+ public void computeDetail(IValue value, IValueDetailListener listener) {
+ String detail = ""; //$NON-NLS-1$
+ if (value instanceof Value) {
+ // Avoid quoting string JavaScript values by getting the value string
+ // from the underlying JsValue.
+ detail = ((Value) value).getJsValue().getValueString();
+ }
+
+ listener.detailComputed(value, detail);
+ }
+
+ public IEditorInput getEditorInput(Object element) {
+ return toEditorInput(element);
+ }
+
+ public static IEditorInput toEditorInput(Object element) {
+ if (element instanceof IFile) {
+ return new FileEditorInput((IFile) element);
+ }
+
+ if (element instanceof ILineBreakpoint) {
+ return new FileEditorInput(
+ (IFile) ((ILineBreakpoint) element).getMarker().getResource());
+ }
+
+ return null;
+ }
+
+ public String getEditorId(IEditorInput input, Object element) {
+ IFile file = null;
+ if (element instanceof IFile) {
+ file = (IFile) element;
+ } else if (element instanceof IBreakpoint) {
+ // Ñan the breakpoint marker be on the folder/project? Everything is possible with plugins...
+ IResource resource = ((IBreakpoint) element).getMarker().getResource();
+ if (resource instanceof IFile) {
+ file = (IFile) resource;
+ }
+ }
+ if (file != null) {
+ // Notice that this method will pick the editor not only on extension mapping basis but also user preference
+ try {
+ return IDE.getEditorDescriptor(file).getId();
+ } catch (PartInitException e) {
+ return JsEditor.EDITOR_ID;
+ }
+ }
+
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/JsEvalContextManager.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,189 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.chromium.debug.core.model.StackFrame;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.contexts.DebugContextEvent;
+import org.eclipse.debug.ui.contexts.IDebugContextListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IWindowListener;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * Keeps track of the evaluation context (selected StackFrame) in all the
+ * workbench parts. A singleton.
+ */
+public class JsEvalContextManager implements IWindowListener, IDebugContextListener {
+
+ private static final String DEBUGGER_ACTIVE = ChromiumDebugUIPlugin.PLUGIN_ID + ".debuggerActive"; //$NON-NLS-1$
+
+ private static JsEvalContextManager instance;
+
+ private IWorkbenchWindow activeWindow;
+
+ private final Map<IWorkbenchPage, StackFrame> pageToFrame =
+ new HashMap<IWorkbenchPage, StackFrame>();
+
+ protected JsEvalContextManager() {
+ DebugUITools.getDebugContextManager().addDebugContextListener(this);
+ }
+
+ /**
+ * This method will get called only once.
+ */
+ public static void startup() {
+ Runnable r = new Runnable() {
+ public void run() {
+ if (instance == null) {
+ instance = new JsEvalContextManager();
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ workbench.addWindowListener(instance);
+ instance.activeWindow = workbench.getActiveWorkbenchWindow();
+ }
+ }
+ };
+ ChromiumDebugUIPlugin.getDisplay().asyncExec(r);
+ }
+
+ public void windowActivated(IWorkbenchWindow window) {
+ activeWindow = window;
+ }
+
+ public void windowClosed(IWorkbenchWindow window) {
+ }
+
+ public void windowDeactivated(IWorkbenchWindow window) {
+ }
+
+ public void windowOpened(IWorkbenchWindow window) {
+ }
+
+ public void debugContextChanged(DebugContextEvent event) {
+ if ((event.getFlags() & DebugContextEvent.ACTIVATED) > 0) {
+ IWorkbenchPart part = event.getDebugContextProvider().getPart();
+ if (part == null) {
+ return;
+ }
+ IWorkbenchPage page = part.getSite().getPage();
+ ISelection selection = event.getContext();
+ if (selection instanceof IStructuredSelection) {
+ Object firstElement = ((IStructuredSelection) selection).getFirstElement();
+ if (firstElement instanceof IAdaptable) {
+ StackFrame frame = (StackFrame) ((IAdaptable) firstElement).getAdapter(StackFrame.class);
+ if (frame != null) {
+ putStackFrame(page, frame);
+ return;
+ }
+ }
+ }
+ // debug context for the |page| has been lost
+ removeStackFrame(page);
+ }
+ }
+
+ /**
+ * Returns the stackframe corresponding to the given {@code part}, or {@code
+ * null} if none.
+ *
+ * @param part the active part
+ * @return the stack frame in whose context the evaluation is performed, or
+ * {@code null} if none
+ */
+ public static StackFrame getStackFrameFor(IWorkbenchPart part) {
+ IWorkbenchPage page = part.getSite().getPage();
+ StackFrame frame = getStackFrameFor(page);
+ if (frame == null) {
+ return getStackFrameFor(page.getWorkbenchWindow());
+ }
+ return frame;
+ }
+
+ /**
+ * Returns the stackframe corresponding to the given {@code window}, or
+ * {@code null} if none.
+ *
+ * @param window to find the StackFrame for. If {@code null}, the {@code
+ * activeWindow} will be used instead
+ * @return the stack frame in whose the evaluation is performed, or {@code
+ * null} if none
+ */
+ public static StackFrame getStackFrameFor(IWorkbenchWindow window) {
+ Set<IWorkbenchWindow> visitedWindows = new HashSet<IWorkbenchWindow>();
+ if (window == null) {
+ window = instance.activeWindow;
+ }
+ return getStackFrameFor(window, visitedWindows);
+ }
+
+ private static StackFrame getStackFrameFor(
+ IWorkbenchWindow window, Set<IWorkbenchWindow> visitedWindows) {
+ IWorkbenchPage activePage = window.getActivePage();
+ StackFrame frame = null;
+ // Check the active page in the window
+ if (activePage != null) {
+ frame = getStackFrameFor(activePage);
+ if (frame != null) {
+ return frame;
+ }
+ }
+ // Check all the current Eclipse window pages
+ for (IWorkbenchPage windowPage : window.getPages()) {
+ if (activePage != windowPage) {
+ frame = getStackFrameFor(windowPage);
+ if (frame != null) {
+ return frame;
+ }
+ }
+ }
+
+ // Last resort - check all other Eclipse windows
+ visitedWindows.add(window);
+
+ for (IWorkbenchWindow workbenchWindow : PlatformUI.getWorkbench().getWorkbenchWindows()) {
+ if (!visitedWindows.contains(workbenchWindow)) {
+ frame = getStackFrameFor(workbenchWindow, visitedWindows);
+ if (frame != null) {
+ return frame;
+ }
+ }
+ }
+
+ // Nothing found
+ return null;
+ }
+
+ private static StackFrame getStackFrameFor(IWorkbenchPage page) {
+ if (instance != null) {
+ return instance.pageToFrame.get(page);
+ }
+ return null;
+ }
+
+ private void removeStackFrame(IWorkbenchPage page) {
+ pageToFrame.remove(page);
+ if (pageToFrame.isEmpty()) {
+ // No more available frames
+ System.setProperty(DEBUGGER_ACTIVE, Boolean.FALSE.toString());
+ }
+ }
+
+ private void putStackFrame(IWorkbenchPage page, StackFrame frame) {
+ pageToFrame.put(page, frame);
+ System.setProperty(DEBUGGER_ACTIVE, Boolean.TRUE.toString());
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/JsWatchExpressionDelegate.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,166 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.debug.core.model.DebugElementImpl;
+import org.chromium.debug.core.model.StackFrame;
+import org.chromium.debug.core.model.Variable;
+import org.chromium.sdk.CallFrame;
+import org.chromium.sdk.JsVariable;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.model.IDebugElement;
+import org.eclipse.debug.core.model.IValue;
+import org.eclipse.debug.core.model.IWatchExpressionDelegate;
+import org.eclipse.debug.core.model.IWatchExpressionListener;
+import org.eclipse.debug.core.model.IWatchExpressionResult;
+
+/**
+ * Performs the Watch expression evaluation while debugging Chromium JavaScript.
+ */
+public class JsWatchExpressionDelegate implements IWatchExpressionDelegate {
+
+ private static final String[] EMPTY_STRINGS = new String[0];
+
+ private static final class GoodWatchExpressionResult implements IWatchExpressionResult {
+
+ private final Variable variable;
+
+ private final String expression;
+
+ private IValue value;
+
+ private DebugException exception;
+
+ private GoodWatchExpressionResult(Variable variable, String expression) {
+ this.variable = variable;
+ this.expression = expression;
+ }
+
+ public String[] getErrorMessages() {
+ return exception == null
+ ? EMPTY_STRINGS
+ : new String[] { exception.getStatus().getMessage() };
+ }
+
+ public DebugException getException() {
+ getValue();
+ return exception;
+ }
+
+ public String getExpressionText() {
+ return expression;
+ }
+
+ public synchronized IValue getValue() {
+ if (value == null && exception == null) {
+ try {
+ value = variable.getValue();
+ } catch (DebugException e) {
+ this.exception = e;
+ }
+ }
+ return value;
+ }
+
+ public boolean hasErrors() {
+ getValue();
+ return exception != null;
+ }
+ }
+
+ private static final class BadWatchExpressionResult implements IWatchExpressionResult {
+
+ private final DebugException exception;
+
+ private final String expressionText;
+
+ private BadWatchExpressionResult(DebugException exception, String expressionText) {
+ this.exception = exception;
+ this.expressionText = expressionText;
+ }
+
+ public String[] getErrorMessages() {
+ return new String[] { exception.getStatus().getMessage() };
+ }
+
+ public DebugException getException() {
+ return exception;
+ }
+
+ public String getExpressionText() {
+ return expressionText;
+ }
+
+ public IValue getValue() {
+ return null;
+ }
+
+ public boolean hasErrors() {
+ return true;
+ }
+ }
+
+ public void evaluateExpression(final String expression, final IDebugElement context,
+ final IWatchExpressionListener listener) {
+ final DebugElementImpl contextImpl = (DebugElementImpl) context;
+ if (!contextImpl.getDebugTarget().isSuspended()) {
+ // can only evaluate while suspended. Notify empty result.
+ listener.watchEvaluationFinished(new IWatchExpressionResult() {
+
+ public String[] getErrorMessages() {
+ return EMPTY_STRINGS;
+ }
+
+ public DebugException getException() {
+ return null;
+ }
+
+ public String getExpressionText() {
+ return expression;
+ }
+
+ public IValue getValue() {
+ return null;
+ }
+
+ public boolean hasErrors() {
+ return false;
+ }
+ });
+ return;
+ }
+ if (!(contextImpl instanceof StackFrame)) {
+ listener.watchEvaluationFinished(new BadWatchExpressionResult(
+ new DebugException(
+ new Status(Status.ERROR, ChromiumDebugUIPlugin.PLUGIN_ID, "Bad debug context")), //$NON-NLS-1$
+ expression));
+ return;
+ }
+ StackFrame stackFrame = (StackFrame) contextImpl;
+ final CallFrame frame = stackFrame.getCallFrame();
+ frame.evaluateAsync(expression, new CallFrame.EvaluateCallback() {
+ public void success(JsVariable variable) {
+ final Variable var = new Variable(contextImpl.getDebugTarget(), variable, false);
+ listener.watchEvaluationFinished(new GoodWatchExpressionResult(var, expression));
+ }
+
+ public void failure(String message) {
+ listener.watchEvaluationFinished(new BadWatchExpressionResult(new DebugException(
+ createErrorStatus(message == null
+ ? Messages.JsWatchExpressionDelegate_ErrorEvaluatingExpression
+ : message, null)), expression));
+ return;
+ }
+ },
+ null);
+ }
+
+ private static Status createErrorStatus(String message, Exception e) {
+ return new Status(Status.ERROR, ChromiumDebugPlugin.PLUGIN_ID, message, e);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/Messages.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,32 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * NLS messages for the package.
+ */
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME =
+ "org.chromium.debug.ui.messages"; //$NON-NLS-1$
+
+ public static String ChromiumDebugUIPlugin_Error;
+
+ public static String ChromiumDebugUIPlugin_Info;
+
+ public static String ChromiumDebugUIPlugin_Warning;
+
+ public static String JsWatchExpressionDelegate_BadStackStructureWhileEvaluating;
+
+ public static String JsWatchExpressionDelegate_ErrorEvaluatingExpression;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/PluginUtil.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,83 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.debug.core.util.ChromiumDebugPluginUtil;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * This class provides generic plugin-wide services.
+ */
+public class PluginUtil {
+
+ private static final String PROJECT_EXPLORER_ID = "org.eclipse.ui.navigator.ProjectExplorer"; //$NON-NLS-1$
+
+ /**
+ * Brings up the "Project Explorer" view in the active workbench window.
+ */
+ public static void openProjectExplorerView() {
+ Display.getDefault().asyncExec(new Runnable() {
+ public void run() {
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+ if (window == null) {
+ if (workbench.getWorkbenchWindowCount() == 1) {
+ window = workbench.getWorkbenchWindows()[0];
+ }
+ }
+ if (window != null) {
+ try {
+ window.getActivePage().showView(PROJECT_EXPLORER_ID);
+ } catch (PartInitException e) {
+ // ignore
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Determines whether {@code file} is a .chromium file in a
+ * JavaScript debug project.
+ *
+ * @param file to test
+ * @return whether the file extension is ".chromium" and its project has the
+ * ChromiumDebugPluginUtil#JS_DEBUG_PROJECT_NATURE nature
+ */
+ public static boolean isChromiumDebugFile(IFile file) {
+ IProject project = file.getProject();
+ try {
+ return (project.hasNature(ChromiumDebugPluginUtil.JS_DEBUG_PROJECT_NATURE) &&
+ file.getName().endsWith(ChromiumDebugPluginUtil.CHROMIUM_EXTENSION_SUFFIX));
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ return false;
+ }
+ }
+
+
+ /**
+ * Removes the ".chromium" extension from the fileName.
+ *
+ * @param fileName to remove the extension from
+ * @return a file name without the ".chromium" extension
+ */
+ public static String stripChromiumExtension(String fileName) {
+ return fileName.substring(
+ 0, fileName.length() - ChromiumDebugPluginUtil.CHROMIUM_EXTENSION_SUFFIX.length());
+ }
+
+ private PluginUtil() {
+ // not instantiable
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/JsBreakpointPropertiesRulerAction.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,64 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.actions;
+
+import org.chromium.debug.core.model.ChromiumLineBreakpoint;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.ui.actions.RulerBreakpointAction;
+import org.eclipse.jface.text.source.IVerticalRulerInfo;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.ui.dialogs.PropertyDialogAction;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.ui.texteditor.IUpdate;
+
+/**
+ * Action to bring up the breakpoint properties dialog.
+ */
+public class JsBreakpointPropertiesRulerAction extends RulerBreakpointAction implements IUpdate {
+
+ private IBreakpoint breakpoint;
+
+ public JsBreakpointPropertiesRulerAction(ITextEditor editor, IVerticalRulerInfo rulerInfo) {
+ super(editor, rulerInfo);
+ setText(Messages.JsBreakpointPropertiesRulerAction_ItemLabel);
+ }
+
+ @Override
+ public void run() {
+ if (getBreakpoint() != null) {
+ PropertyDialogAction action =
+ new PropertyDialogAction(getEditor().getEditorSite(),
+ new ISelectionProvider() {
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {
+ }
+
+ public ISelection getSelection() {
+ return new StructuredSelection(getBreakpoint());
+ }
+
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+ }
+
+ public void setSelection(ISelection selection) {
+ }
+ });
+ action.run();
+ }
+ }
+
+ public void update() {
+ breakpoint = null;
+ IBreakpoint activeBreakpoint = getBreakpoint();
+ if (activeBreakpoint != null &&
+ activeBreakpoint instanceof ChromiumLineBreakpoint) {
+ breakpoint = activeBreakpoint;
+ }
+ setEnabled(breakpoint != null);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/JsBreakpointPropertiesRulerActionDelegate.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,22 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.actions;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.text.source.IVerticalRulerInfo;
+import org.eclipse.ui.texteditor.AbstractRulerActionDelegate;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Delegate for JsBreakpointPropertiesRulerAction.
+ */
+public class JsBreakpointPropertiesRulerActionDelegate extends AbstractRulerActionDelegate {
+
+ @Override
+ protected IAction createAction(ITextEditor editor, IVerticalRulerInfo rulerInfo) {
+ return new JsBreakpointPropertiesRulerAction(editor, rulerInfo);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/JsInspectExpression.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,104 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.actions;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.debug.core.model.StackFrame;
+import org.chromium.debug.core.model.Value;
+import org.chromium.sdk.JsVariable;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.IDebugEventSetListener;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IDebugElement;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IErrorReportingExpression;
+import org.eclipse.debug.core.model.IValue;
+
+/**
+ * An Eclipse object for the JavaScript inspected expression.
+ */
+public class JsInspectExpression extends PlatformObject
+ implements IErrorReportingExpression, IDebugEventSetListener {
+
+ private final StackFrame stackFrame;
+
+ private final JsVariable variable;
+
+ private final String errorMessage;
+
+ private final String expression;
+
+ public JsInspectExpression(StackFrame stackFrame, String expression, JsVariable variable,
+ String errorMessage) {
+ this.stackFrame = stackFrame;
+ this.expression = expression;
+ this.variable = variable;
+ this.errorMessage = errorMessage;
+ }
+
+ public String[] getErrorMessages() {
+ return errorMessage == null
+ ? new String[0]
+ : new String[] { errorMessage };
+ }
+
+ public boolean hasErrors() {
+ return errorMessage != null;
+ }
+
+ public void dispose() {
+ }
+
+ public IDebugTarget getDebugTarget() {
+ IValue value = getValue();
+ if (value != null) {
+ return value.getDebugTarget();
+ }
+ return null;
+ }
+
+ public String getExpressionText() {
+ return expression;
+ }
+
+ public IValue getValue() {
+ return variable != null
+ ? Value.create(stackFrame.getDebugTarget(), variable.getValue())
+ : null;
+ }
+
+ public ILaunch getLaunch() {
+ return getValue().getLaunch();
+ }
+
+ public String getModelIdentifier() {
+ return ChromiumDebugPlugin.DEBUG_MODEL_ID;
+ }
+
+ public void handleDebugEvents(DebugEvent[] events) {
+ for (DebugEvent event : events) {
+ switch (event.getKind()) {
+ case DebugEvent.TERMINATE:
+ if (event.getSource().equals(getDebugTarget())) {
+ DebugPlugin.getDefault().getExpressionManager().removeExpression(this);
+ }
+ break;
+ case DebugEvent.SUSPEND:
+ if (event.getDetail() != DebugEvent.EVALUATION_IMPLICIT &&
+ event.getSource() instanceof IDebugElement) {
+ IDebugElement source = (IDebugElement) event.getSource();
+ if (source.getDebugTarget().equals(getDebugTarget())) {
+ DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] {
+ new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT) });
+ }
+ }
+ break;
+ }
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/JsInspectSnippetAction.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,283 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.actions;
+
+import org.chromium.debug.core.model.StackFrame;
+import org.chromium.debug.ui.ChromiumDebugUIPlugin;
+import org.chromium.debug.ui.JsEvalContextManager;
+import org.chromium.debug.ui.editors.JavascriptUtil;
+import org.chromium.sdk.CallFrame;
+import org.chromium.sdk.JsVariable;
+import org.eclipse.debug.core.model.IExpression;
+import org.eclipse.debug.ui.DebugPopup;
+import org.eclipse.debug.ui.InspectPopupDialog;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorActionDelegate;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IViewActionDelegate;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Action for inspecting a JavaScript snippet.
+ */
+public class JsInspectSnippetAction implements IEditorActionDelegate,
+ IWorkbenchWindowActionDelegate, IPartListener, IViewActionDelegate, CallFrame.EvaluateCallback {
+
+ private static final String ACTION_DEFINITION_ID = "org.chromium.debug.ui.commands.Inspect"; //$NON-NLS-1$
+
+ private IWorkbenchWindow window;
+
+ private IWorkbenchPart targetPart;
+
+ private IAction action;
+
+ private String selectedText;
+
+ private IExpression expression;
+
+ private ITextEditor textEditor;
+
+ private ISelection originalSelection;
+
+ public void setActiveEditor(IAction action, IEditorPart targetEditor) {
+ this.action = action;
+ setTargetPart(targetEditor);
+ }
+
+ public void run(IAction action) {
+ updateAction();
+ run();
+ }
+
+ public void selectionChanged(IAction action, ISelection selection) {
+ this.action = action;
+ }
+
+ public void dispose() {
+ IWorkbenchWindow win = getWindow();
+ if (win != null) {
+ win.getPartService().removePartListener(this);
+ }
+ }
+
+ private IWorkbenchWindow getWindow() {
+ return window;
+ }
+
+ public void init(IWorkbenchWindow window) {
+ this.window = window;
+ IWorkbenchPage page = window.getActivePage();
+ if (page != null) {
+ setTargetPart(page.getActivePart());
+ }
+ window.getPartService().addPartListener(this);
+ }
+
+ public void partActivated(IWorkbenchPart part) {
+ setTargetPart(part);
+ }
+
+ public void partBroughtToTop(IWorkbenchPart part) {
+
+ }
+
+ public void partClosed(IWorkbenchPart part) {
+ if (part == getTargetPart()) {
+ setTargetPart(null);
+ }
+ }
+
+ private IWorkbenchPart getTargetPart() {
+ return targetPart;
+ }
+
+ public void partDeactivated(IWorkbenchPart part) {
+ }
+
+ public void partOpened(IWorkbenchPart part) {
+ }
+
+ private void setTargetPart(IWorkbenchPart part) {
+ this.targetPart = part;
+ }
+
+ public void init(IViewPart view) {
+ setTargetPart(view);
+ }
+
+ private void updateAction() {
+ if (action != null) {
+ retrieveSelection();
+ }
+ }
+
+ protected ISelection getTargetSelection() {
+ IWorkbenchPart part = getTargetPart();
+ if (part != null) {
+ ISelectionProvider provider = part.getSite().getSelectionProvider();
+ if (provider != null) {
+ return provider.getSelection();
+ }
+ }
+ return null;
+ }
+
+ private StackFrame getStackFrameContext() {
+ IWorkbenchPart part = getTargetPart();
+ return getStackFrameForPart(part);
+ }
+
+ private StackFrame getStackFrameForPart(IWorkbenchPart part) {
+ StackFrame frame = part == null
+ ? JsEvalContextManager.getStackFrameFor(getWindow())
+ : JsEvalContextManager.getStackFrameFor(part);
+ return frame;
+ }
+
+ private void run() {
+ getStackFrameContext().getCallFrame().evaluateAsync(getSelectedText(), this, null);
+ }
+
+ protected String getSelectedText() {
+ return selectedText;
+ }
+
+ protected Shell getShell() {
+ if (getTargetPart() != null) {
+ return getTargetPart().getSite().getShell();
+ }
+ return ChromiumDebugUIPlugin.getActiveWorkbenchShell();
+ }
+
+ private void retrieveSelection() {
+ ISelection targetSelection = getTargetSelection();
+ if (targetSelection instanceof ITextSelection) {
+ ITextSelection ts = (ITextSelection) targetSelection;
+ String text = ts.getText();
+ if (textHasContent(text)) {
+ selectedText = text;
+ } else if (getTargetPart() instanceof IEditorPart) {
+ IEditorPart editor = (IEditorPart) getTargetPart();
+ if (editor instanceof ITextEditor) {
+ selectedText = extractSurroundingWord(ts, (ITextEditor) editor);
+ }
+ }
+ }
+ }
+
+ private String extractSurroundingWord(ITextSelection targetSelection, ITextEditor editor) {
+ return JavascriptUtil.extractSurroundingJsIdentifier(
+ editor.getDocumentProvider().getDocument(editor.getEditorInput()),
+ targetSelection.getOffset());
+ }
+
+ private boolean textHasContent(String text) {
+ return text != null && JavascriptUtil.ID_PATTERN.matcher(text).find();
+ }
+
+ public void success(JsVariable var) {
+ if (ChromiumDebugUIPlugin.getDefault() == null) {
+ return;
+ }
+ if (var != null) {
+ if (ChromiumDebugUIPlugin.getDisplay().isDisposed()) {
+ return;
+ }
+ displayResult(var, null);
+ }
+ }
+
+ public void failure(String errorMessage) {
+ displayResult(null, errorMessage);
+ }
+
+ protected void displayResult(final JsVariable var, String errorMessage) {
+ IWorkbenchPart part = getTargetPart();
+ final StyledText styledText = getStyledText(part);
+ if (styledText == null) {
+ return; // TODO(apavlov): fix this when adding inspected variables
+ } else {
+ expression = new JsInspectExpression(getStackFrameContext(), selectedText, var, errorMessage);
+ ChromiumDebugUIPlugin.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ showPopup(styledText);
+ }
+ });
+ }
+ }
+
+ protected void showPopup(StyledText textWidget) {
+ IWorkbenchPart part = getTargetPart();
+ if (part instanceof ITextEditor) {
+ textEditor = (ITextEditor) part;
+ originalSelection = getTargetSelection();
+ }
+ DebugPopup displayPopup =
+ new InspectPopupDialog(getShell(), getPopupAnchor(textWidget), ACTION_DEFINITION_ID,
+ expression) {
+ @Override
+ public boolean close() {
+ boolean returnValue = super.close();
+ if (textEditor != null && originalSelection != null) {
+ textEditor.getSelectionProvider().setSelection(originalSelection);
+ textEditor = null;
+ originalSelection = null;
+ }
+ return returnValue;
+ }
+ };
+ displayPopup.open();
+ }
+
+ private StyledText getStyledText(IWorkbenchPart part) {
+ ITextViewer viewer = (ITextViewer) part.getAdapter(ITextViewer.class);
+ StyledText textWidget = null;
+ if (viewer == null) {
+ Control control = (Control) part.getAdapter(Control.class);
+ if (control instanceof StyledText) {
+ textWidget = (StyledText) control;
+ }
+ } else {
+ textWidget = viewer.getTextWidget();
+ }
+ return textWidget;
+ }
+
+ private static Point getPopupAnchor(StyledText textWidget) {
+ if (textWidget != null) {
+ Point docRange = textWidget.getSelectionRange();
+ int midOffset = docRange.x + (docRange.y / 2);
+ Point point = textWidget.getLocationAtOffset(midOffset);
+ point = textWidget.toDisplay(point);
+ point.y += getFontHeight(textWidget);
+ return point;
+ }
+ return null;
+ }
+
+ private static int getFontHeight(StyledText textWidget) {
+ GC gc = new GC(textWidget);
+ gc.setFont(textWidget.getFont());
+ int height = gc.getFontMetrics().getHeight();
+ gc.dispose();
+ return height;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/Messages.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,36 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.actions;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * NLS messages for the package.
+ */
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME =
+ "org.chromium.debug.ui.actions.messages"; //$NON-NLS-1$
+
+ public static String ExpressionEvaluator_CannotEvaluateWhenNotSuspended;
+
+ public static String ExpressionEvaluator_ErrorEvaluatingExpression;
+
+ public static String ExpressionEvaluator_ErrorInspectingObject;
+
+ public static String ExpressionEvaluator_EvaluationThreadInterrupted;
+
+ public static String ExpressionEvaluator_SocketError;
+
+ public static String ExpressionEvaluator_UnableToEvaluateExpression;
+
+ public static String JsBreakpointPropertiesRulerAction_ItemLabel;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/OpenFunctionAction.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,125 @@
+package org.chromium.debug.ui.actions;
+
+import org.chromium.debug.core.model.Variable;
+import org.chromium.debug.ui.JsDebugModelPresentation;
+import org.chromium.debug.ui.editors.JsEditor;
+import org.chromium.sdk.JsFunction;
+import org.chromium.sdk.JsObject;
+import org.chromium.sdk.JsValue;
+import org.chromium.sdk.JsVariable;
+import org.chromium.sdk.Script;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.ui.IActionDelegate2;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * The action for context view in Variable view that opens selected function source text in editor.
+ */
+public class OpenFunctionAction implements IObjectActionDelegate, IActionDelegate2 {
+ private Runnable currentRunnable = null;
+
+ public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+ }
+
+ public void run(IAction action) {
+ if (currentRunnable == null) {
+ return;
+ }
+ currentRunnable.run();
+ }
+
+ public void selectionChanged(IAction action, ISelection selection) {
+ final Variable variable = getVariableFromSelection(selection);
+ final JsFunction jsFunction = getJsFunctionFromVariable(variable);
+
+ currentRunnable = createRunnable(variable, jsFunction);
+ action.setEnabled(currentRunnable != null);
+ }
+
+ private Runnable createRunnable(final Variable variable, final JsFunction jsFunction) {
+ if (jsFunction == null) {
+ return null;
+ }
+ return new Runnable() {
+
+ public void run() {
+ // This works in UI thread.
+ IWorkbench workbench = PlatformUI.getWorkbench();
+ final IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow();
+
+ Script script = jsFunction.getScript();
+ if (script == null) {
+ return;
+ }
+ IFile resource = variable.getDebugTarget().getResourceManager().getResource(script);
+ IEditorInput input = JsDebugModelPresentation.toEditorInput(resource);
+ IEditorPart editor;
+ try {
+ editor = activeWorkbenchWindow.getActivePage().openEditor(input, JsEditor.EDITOR_ID);
+ } catch (PartInitException e) {
+ throw new RuntimeException(e);
+ }
+ if (editor instanceof ITextEditor == false) {
+ return;
+ }
+ ITextEditor textEditor = (ITextEditor) editor;
+ textEditor.selectAndReveal(jsFunction.getSourcePosition(), 0);
+ }
+ };
+ }
+
+ public void dispose() {
+ currentRunnable = null;
+ }
+
+ public void init(IAction action) {
+ }
+
+ public void runWithEvent(IAction action, Event event) {
+ if (currentRunnable == null) {
+ return;
+ }
+ currentRunnable.run();
+ }
+
+ private JsFunction getJsFunctionFromVariable(Variable variable) {
+ if (variable == null) {
+ return null;
+ }
+ JsVariable jsVariable = variable.getJsVariable();
+ JsValue jsValue = jsVariable.getValue();
+ JsObject jsObject = jsValue.asObject();
+ if (jsObject == null) {
+ return null;
+ }
+ return jsObject.asFunction();
+ }
+
+ private Variable getVariableFromSelection(ISelection selection) {
+ if (selection instanceof IStructuredSelection == false) {
+ return null;
+ }
+ IStructuredSelection structuredSelection = (IStructuredSelection) selection;
+ if (structuredSelection.size() != 1) {
+ // We do not support multiple selection.
+ return null;
+ }
+ Object element = structuredSelection.getFirstElement();
+ if (element instanceof Variable == false) {
+ return null;
+ }
+ return (Variable) element;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/actions/messages.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,7 @@
+ExpressionEvaluator_CannotEvaluateWhenNotSuspended=JavaScript thread not suspended. Cannot perform evaluation.
+ExpressionEvaluator_ErrorEvaluatingExpression=Error evaluating expression
+ExpressionEvaluator_ErrorInspectingObject=Error inspecting object
+ExpressionEvaluator_EvaluationThreadInterrupted=Evaluation thread interrupted
+ExpressionEvaluator_SocketError=Socket error while evaluating expression
+ExpressionEvaluator_UnableToEvaluateExpression=Unable to evaluate expression
+JsBreakpointPropertiesRulerAction_ItemLabel=Breakpoint Properties...
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/editors/EditorColors.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,38 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.editors;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Converts RGB to Color, reuses the existing Color instances.
+ * A singleton.
+ */
+public class EditorColors {
+
+ private static final Map<Integer, Color> intToColor = new HashMap<Integer, Color>();
+
+ public static Color getColor(RGB rgb) {
+ Integer colorInt = rgbToInteger(rgb);
+ Color color = intToColor.get(colorInt);
+ if (color == null) {
+ color = new Color(Display.getDefault(), rgb);
+ intToColor.put(colorInt, color);
+ }
+ return color;
+ }
+
+ private static Integer rgbToInteger(RGB rgb) {
+ return
+ ((rgb.red & 0xFF) << 16) +
+ ((rgb.green & 0xFF) << 8) +
+ (rgb.blue & 0xFF);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JavascriptUtil.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,142 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.editors;
+
+import java.util.regex.Pattern;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
+
+/**
+ * A utility for handling JavaScript-related data.
+ */
+public class JavascriptUtil {
+
+ private static final String OPEN_BRACKET = "["; //$NON-NLS-1$
+
+ private static final String CLOSE_BRACKET = "]"; //$NON-NLS-1$
+
+ private static final String ID_CHARS_REGEX = "\\p{L}_$\\d"; //$NON-NLS-1$
+
+ private static final String QUALIFIED_ID_CHARS_REGEX = ID_CHARS_REGEX + "\\.\\[\\]"; //$NON-NLS-1$
+
+ /**
+ * Contains chars acceptable as part of expression to inspect to the right of
+ * the cursor.
+ */
+ public static final Pattern ID_PATTERN =
+ Pattern.compile(OPEN_BRACKET + ID_CHARS_REGEX + CLOSE_BRACKET);
+
+ /**
+ * Contains chars acceptable as part of expression to inspect to the left of
+ * the cursor.
+ */
+ public static final Pattern QUALIFIED_ID_PATTERN =
+ Pattern.compile(OPEN_BRACKET + QUALIFIED_ID_CHARS_REGEX + CLOSE_BRACKET);
+
+ public static boolean isJsIdentifierCharacter(char ch, boolean qualified) {
+ return qualified
+ ? QUALIFIED_ID_PATTERN.matcher(String.valueOf(ch)).find()
+ : ID_PATTERN.matcher(String.valueOf(ch)).find();
+ }
+
+ /**
+ * Returns a JavaScript qualified identifier surrounding the character at
+ * {@code offset} position in the given {@code document}.
+ *
+ * @param document to extract an identifier from
+ * @param offset of the pivot character (before, in, or after the identifier)
+ * @return JavaScript identifier, or {@code null} if none found
+ */
+ public static String extractSurroundingJsIdentifier(IDocument document, int offset) {
+ IRegion region = getSurroundingIdentifierRegion(document, offset, true);
+ try {
+ return region == null
+ ? null
+ : document.get(region.getOffset(), region.getLength());
+ } catch (BadLocationException e) {
+ ChromiumDebugPlugin.log(e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns a region enclosing a JavaScript identifier found in {@code doc} at
+ * the {@code offset} position. If {@code qualified == true}, all leading
+ * qualifying names will be included into the region, otherwise the member
+ * operator (".") will be considered as an identifier terminator.
+ *
+ * @param doc the document to extract an identifier region from
+ * @param offset of the pivot character (before, in, or after the identifier)
+ * @param qualified whether to read qualified identifiers rather than simple
+ * ones
+ * @return an IRegion corresponding to the JavaScript identifier overlapping
+ * offset, or null if none
+ */
+ public static IRegion getSurroundingIdentifierRegion(
+ IDocument doc, int offset, boolean qualified) {
+ if (doc == null) {
+ return null;
+ }
+ try {
+ int squareBrackets = 0;
+ char ch = doc.getChar(offset);
+ if (!isJsIdentifierCharacter(ch, qualified) && offset > 0) {
+ --offset; // cursor is AFTER the identifier
+ }
+ int start = offset;
+ int end = offset;
+ int goodStart = offset;
+ while (start >= 0) {
+ ch = doc.getChar(start);
+ if (!isJsIdentifierCharacter(ch, qualified)) {
+ break;
+ }
+ if (ch == '[') {
+ squareBrackets--;
+ } else if (ch == ']') {
+ squareBrackets++;
+ }
+ if (squareBrackets < 0) {
+ break;
+ }
+ goodStart = start;
+ --start;
+ }
+ start = goodStart;
+
+ int length = doc.getLength();
+ while (end < length) {
+ try {
+ ch = doc.getChar(end);
+ if (!isJsIdentifierCharacter(ch, false)) {
+ // stop at the current name qualifier
+ // rather than scan through the entire qualified id
+ break;
+ }
+ ++end;
+ } catch (BadLocationException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ }
+ if (start >= end) {
+ return null;
+ } else {
+ return new Region(start, end - start);
+ }
+ } catch (BadLocationException e) {
+ ChromiumDebugPlugin.log(e);
+ return null;
+ }
+ }
+
+ private JavascriptUtil() {
+ // not instantiable
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JsCodeScanner.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,149 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.editors;
+
+import org.eclipse.jface.text.TextAttribute;
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner;
+import org.eclipse.jface.text.rules.EndOfLineRule;
+import org.eclipse.jface.text.rules.IRule;
+import org.eclipse.jface.text.rules.IWhitespaceDetector;
+import org.eclipse.jface.text.rules.IWordDetector;
+import org.eclipse.jface.text.rules.MultiLineRule;
+import org.eclipse.jface.text.rules.NumberRule;
+import org.eclipse.jface.text.rules.SingleLineRule;
+import org.eclipse.jface.text.rules.Token;
+import org.eclipse.jface.text.rules.WhitespaceRule;
+import org.eclipse.jface.text.rules.WordRule;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.RGB;
+
+/**
+ * JavaScript code scanner for source code highlighting.
+ */
+public class JsCodeScanner extends BufferedRuleBasedScanner {
+
+ private Token commentToken;
+
+ private final TextAttribute commentAttribute =
+ new TextAttribute(EditorColors.getColor(new RGB(63, 127, 95)), null, SWT.NORMAL);
+
+ private final TextAttribute jsDocAttribute =
+ new TextAttribute(EditorColors.getColor(new RGB(127,127,159)), null, SWT.NORMAL);
+
+ public JsCodeScanner() {
+ createRules();
+ }
+
+ public TextAttribute getCommentAttribute() {
+ return commentAttribute;
+ }
+
+ public TextAttribute getJsDocAttribute() {
+ return jsDocAttribute;
+ }
+
+ /**
+ * Use the default Eclipse higlighting scheme.
+ */
+ private void createRules() {
+ Token keywordToken = new Token(
+ new TextAttribute(EditorColors.getColor(new RGB(127, 0, 85)), null, SWT.BOLD));
+
+ commentToken = new Token(commentAttribute);
+
+ Token jsDocToken = new Token(jsDocAttribute);
+
+ Token stringToken = new Token(
+ new TextAttribute(EditorColors.getColor(new RGB(42, 0, 255)), null, SWT.NORMAL));
+
+ RGB blackRgb = new RGB(0, 0, 0);
+
+ Token numberToken = new Token(
+ new TextAttribute(EditorColors.getColor(blackRgb), null, SWT.NORMAL));
+
+ Token normalToken = new Token(
+ new TextAttribute(EditorColors.getColor(blackRgb), null, SWT.NORMAL));
+ setDefaultReturnToken(normalToken);
+
+ setRules(new IRule[] {
+ new EndOfLineRule("//", commentToken), //$NON-NLS-1$
+ new KeywordRule(keywordToken),
+ new MultiLineRule("/**", "*/", jsDocToken, (char) 0, false), //$NON-NLS-1$ //$NON-NLS-2$
+ new MultiLineRule("/*", "*/", commentToken, (char) 0, false), //$NON-NLS-1$ //$NON-NLS-2$
+ new SingleLineRule("\"", "\"", stringToken, '\\'), //$NON-NLS-1$ //$NON-NLS-2$
+ // Regexp
+ new SingleLineRule("/", "/", stringToken, '\\'), //$NON-NLS-1$ //$NON-NLS-2$
+ new SingleLineRule("'", "'", stringToken, '\\'), //$NON-NLS-1$ //$NON-NLS-2$
+ new WhitespaceRule(new IWhitespaceDetector() {
+ public boolean isWhitespace(char c) {
+ return Character.isWhitespace(c);
+ }
+ }),
+
+ new WordRule(new WordDetector(), normalToken),
+ new NumberRule(numberToken),
+ });
+ }
+
+ private static class KeywordRule extends WordRule {
+
+ private static final String[] KEYWORDS = {
+ "break", //$NON-NLS-1$
+ "case", //$NON-NLS-1$
+ "catch", //$NON-NLS-1$
+ "const", //$NON-NLS-1$
+ "continue", //$NON-NLS-1$
+ "debugger", //$NON-NLS-1$
+ "default", //$NON-NLS-1$
+ "delete", //$NON-NLS-1$
+ "do", //$NON-NLS-1$
+ "else", //$NON-NLS-1$
+ "false", //$NON-NLS-1$
+ "finally", //$NON-NLS-1$
+ "for", //$NON-NLS-1$
+ "function", //$NON-NLS-1$
+ "if", //$NON-NLS-1$
+ "in", //$NON-NLS-1$
+ "instanceof", //$NON-NLS-1$
+ "new", //$NON-NLS-1$
+ "null", //$NON-NLS-1$
+ "return", //$NON-NLS-1$
+ "switch", //$NON-NLS-1$
+ "this", //$NON-NLS-1$
+ "throw", //$NON-NLS-1$
+ "true", //$NON-NLS-1$
+ "try", //$NON-NLS-1$
+ "typeof", //$NON-NLS-1$
+ "var", //$NON-NLS-1$
+ "void", //$NON-NLS-1$
+ "while", //$NON-NLS-1$
+ "with", //$NON-NLS-1$
+
+ // Highlight important qualifiers
+ "__proto__", //$NON-NLS-1$
+ "prototype", //$NON-NLS-1$
+ };
+
+ public KeywordRule(Token token) {
+ super(new WordDetector());
+
+ for (String word : KEYWORDS) {
+ addWord(word, token);
+ }
+ }
+ }
+
+ private static class WordDetector implements IWordDetector {
+
+ public boolean isWordPart(char c) {
+ return Character.isJavaIdentifierPart(c);
+ }
+
+ public boolean isWordStart(char c) {
+ return Character.isJavaIdentifierStart(c);
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JsDebugTextHover.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,62 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.editors;
+
+import org.chromium.debug.core.model.StackFrame;
+import org.chromium.debug.core.util.JsValueStringifier;
+import org.chromium.sdk.CallFrame;
+import org.chromium.sdk.JsVariable;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextHover;
+import org.eclipse.jface.text.ITextViewer;
+
+/**
+ * Supplies a hover for JavaScript expressions while on a breakpoint.
+ */
+public class JsDebugTextHover implements ITextHover {
+
+ private static final JsValueStringifier STRINGIFIER = new JsValueStringifier();
+
+ public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) {
+ IDocument doc = textViewer.getDocument();
+ String expression = JavascriptUtil.extractSurroundingJsIdentifier(doc, hoverRegion.getOffset());
+ if (expression == null) {
+ return null;
+ }
+
+ IAdaptable context = DebugUITools.getDebugContext();
+ if (context == null) { // debugger not active
+ return null;
+ }
+
+ StackFrame frame = (StackFrame) context.getAdapter(StackFrame.class);
+ if (frame == null) { // not a stackframe-related context
+ return null;
+ }
+
+ final JsVariable[] result = new JsVariable[1];
+ frame.getCallFrame().evaluateSync(expression, new CallFrame.EvaluateCallback() {
+ public void success(JsVariable var) {
+ result[0] = var;
+ }
+ public void failure(String errorMessage) {
+ }
+ });
+ if (result[0] == null) {
+ return null;
+ }
+
+ return STRINGIFIER.render(result[0].getValue());
+ }
+
+ public IRegion getHoverRegion(ITextViewer textViewer, int offset) {
+ IDocument doc = textViewer.getDocument();
+ return JavascriptUtil.getSurroundingIdentifierRegion(doc, offset, false);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JsDocumentProvider.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,30 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.editors;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.jface.text.rules.FastPartitioner;
+import org.eclipse.ui.editors.text.FileDocumentProvider;
+
+/**
+ * Provides JavaScript content and sets up the document partitioner.
+ */
+public class JsDocumentProvider extends FileDocumentProvider {
+
+ @Override
+ protected IDocument createDocument(Object element) throws CoreException {
+ IDocument doc = super.createDocument(element);
+ if (doc != null) {
+ IDocumentPartitioner partitioner = new FastPartitioner(
+ new JsPartitionScanner(), JsPartitionScanner.PARTITION_TYPES);
+ partitioner.connect(doc);
+ doc.setDocumentPartitioner(partitioner);
+ }
+ return doc;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JsEditor.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,43 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.editors;
+
+import org.chromium.debug.ui.PluginUtil;
+import org.eclipse.ui.editors.text.TextEditor;
+
+/**
+ * A simplistic JavaScript editor which supports its own key binding scope.
+ */
+public class JsEditor extends TextEditor {
+
+ /** The ID of this editor as defined in plugin.xml */
+ public static final String EDITOR_ID =
+ "org.chromium.debug.ui.editors.JsEditor"; //$NON-NLS-1$
+
+ /** The ID of the editor context menu */
+ public static final String EDITOR_CONTEXT = EDITOR_ID + ".context"; //$NON-NLS-1$
+
+ /** The ID of the editor ruler context menu */
+ public static final String RULER_CONTEXT = EDITOR_ID + ".ruler"; //$NON-NLS-1$
+
+ @Override
+ protected void initializeEditor() {
+ super.initializeEditor();
+ setEditorContextMenuId(EDITOR_CONTEXT);
+ setRulerContextMenuId(RULER_CONTEXT);
+ setDocumentProvider(new JsDocumentProvider());
+ }
+
+ public JsEditor() {
+ setSourceViewerConfiguration(new JsSourceViewerConfiguration());
+ setKeyBindingScopes(new String[] { "org.eclipse.ui.textEditorScope", //$NON-NLS-1$
+ "org.chromium.debug.ui.editors.JsEditor.context" }); //$NON-NLS-1$
+ }
+
+ @Override
+ protected void setPartName(String partName) {
+ super.setPartName(PluginUtil.stripChromiumExtension(partName));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JsPartitionScanner.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,76 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.editors;
+
+import org.eclipse.jface.text.rules.EndOfLineRule;
+import org.eclipse.jface.text.rules.ICharacterScanner;
+import org.eclipse.jface.text.rules.IPredicateRule;
+import org.eclipse.jface.text.rules.IToken;
+import org.eclipse.jface.text.rules.IWordDetector;
+import org.eclipse.jface.text.rules.MultiLineRule;
+import org.eclipse.jface.text.rules.RuleBasedPartitionScanner;
+import org.eclipse.jface.text.rules.SingleLineRule;
+import org.eclipse.jface.text.rules.Token;
+import org.eclipse.jface.text.rules.WordRule;
+
+/**
+ * JavaScript partition scanner.
+ */
+public class JsPartitionScanner extends RuleBasedPartitionScanner {
+
+ static final String PARTITIONING = "ChromiumJavaScriptPartitioning"; //$NON-NLS-1$
+ static final String MULTILINE_COMMENT= "__js_multiline_comment"; //$NON-NLS-1$
+ static final String JSDOC = "__jsdoc"; //$NON-NLS-1$
+ static final String[] PARTITION_TYPES = {
+ MULTILINE_COMMENT,
+ JSDOC
+ };
+
+ /**
+ * Empty comments should be handled so as not to be confused with JSDoc.
+ */
+ private static class EmptyCommentDetector implements IWordDetector {
+
+ public boolean isWordStart(char c) {
+ return (c == '/');
+ }
+
+ public boolean isWordPart(char c) {
+ return (c == '*' || c == '/');
+ }
+ }
+
+ private static class EmptyCommentPredicateRule extends WordRule implements IPredicateRule {
+ private final IToken successToken;
+
+ public EmptyCommentPredicateRule(IToken successToken) {
+ super(new EmptyCommentDetector());
+ this.successToken = successToken;
+ addWord("/**/", successToken); //$NON-NLS-1$
+ }
+
+ public IToken evaluate(ICharacterScanner scanner, boolean resume) {
+ return super.evaluate(scanner);
+ }
+
+ public IToken getSuccessToken() {
+ return successToken;
+ }
+ }
+
+ public JsPartitionScanner() {
+ IToken jsDocToken= new Token(JSDOC);
+ IToken multilineCommentToken= new Token(MULTILINE_COMMENT);
+
+ setPredicateRules(new IPredicateRule[] {
+ new EndOfLineRule("//", Token.UNDEFINED), //$NON-NLS-1$
+ new SingleLineRule("\"", "\"", Token.UNDEFINED, '\\'), //$NON-NLS-2$ //$NON-NLS-1$
+ new SingleLineRule("'", "'", Token.UNDEFINED, '\\'), //$NON-NLS-2$ //$NON-NLS-1$
+ new EmptyCommentPredicateRule(multilineCommentToken),
+ new MultiLineRule("/**", "*/", jsDocToken, (char) 0, true), //$NON-NLS-1$ //$NON-NLS-2$
+ new MultiLineRule("/*", "*/", multilineCommentToken, (char) 0, true) //$NON-NLS-1$ //$NON-NLS-2$
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JsSourceViewerConfiguration.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,69 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.editors;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextHover;
+import org.eclipse.jface.text.TextAttribute;
+import org.eclipse.jface.text.presentation.IPresentationReconciler;
+import org.eclipse.jface.text.presentation.PresentationReconciler;
+import org.eclipse.jface.text.rules.BufferedRuleBasedScanner;
+import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
+import org.eclipse.jface.text.rules.Token;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
+
+/**
+ * A JavaScript source viewer configuration.
+ */
+public class JsSourceViewerConfiguration extends TextSourceViewerConfiguration {
+
+ private static class MultilineCommentScanner extends BufferedRuleBasedScanner {
+ public MultilineCommentScanner(TextAttribute attr) {
+ setDefaultReturnToken(new Token(attr));
+ }
+ }
+
+ private static final String[] CONTENT_TYPES = new String[] {
+ IDocument.DEFAULT_CONTENT_TYPE,
+ JsPartitionScanner.JSDOC,
+ JsPartitionScanner.MULTILINE_COMMENT
+ };
+
+ private final JsCodeScanner scanner = new JsCodeScanner();
+
+ @Override
+ public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType) {
+ return new JsDebugTextHover();
+ }
+
+ @Override
+ public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) {
+ PresentationReconciler pr = new PresentationReconciler();
+ pr.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer));
+ setDamagerRepairer(pr, new DefaultDamagerRepairer(scanner), IDocument.DEFAULT_CONTENT_TYPE);
+ setDamagerRepairer(
+ pr, new DefaultDamagerRepairer(new MultilineCommentScanner(scanner.getCommentAttribute())),
+ JsPartitionScanner.MULTILINE_COMMENT);
+ setDamagerRepairer(
+ pr, new DefaultDamagerRepairer(new MultilineCommentScanner(scanner.getJsDocAttribute())),
+ JsPartitionScanner.JSDOC);
+ return pr;
+ }
+
+ private void setDamagerRepairer(
+ PresentationReconciler pr,
+ DefaultDamagerRepairer damagerRepairer,
+ String tokenType) {
+ pr.setDamager(damagerRepairer, tokenType);
+ pr.setRepairer(damagerRepairer, tokenType);
+ }
+
+ @Override
+ public String[] getConfiguredContentTypes(ISourceViewer sourceViewer) {
+ return CONTENT_TYPES;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/ChromiumLaunchType.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,101 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+package org.chromium.debug.ui.launcher;
+
+import org.chromium.debug.core.model.JavascriptVmEmbedder;
+import org.chromium.debug.core.model.JavascriptVmEmbedderFactory;
+import org.chromium.debug.core.model.NamedConnectionLoggerFactory;
+import org.chromium.debug.ui.ChromiumDebugUIPlugin;
+import org.chromium.debug.ui.DialogBasedTabSelector;
+import org.chromium.sdk.ConnectionLogger;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.core.Launch;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+public class ChromiumLaunchType extends LaunchTypeBase {
+ @Override
+ protected JavascriptVmEmbedder.ConnectionToRemote createConnectionToRemote(int port,
+ ILaunch launch, boolean addConsoleLogger) throws CoreException {
+ NamedConnectionLoggerFactory consoleFactory =
+ addConsoleLogger ? getLoggerFactory() : NO_CONNECTION_LOGGER_FACTORY;
+ return JavascriptVmEmbedderFactory.connectToChromeDevTools(port, consoleFactory,
+ new DialogBasedTabSelector());
+ }
+
+ private static NamedConnectionLoggerFactory loggerFactory = null;
+
+ private static synchronized NamedConnectionLoggerFactory getLoggerFactory()
+ throws CoreException {
+ if (loggerFactory == null) {
+ loggerFactory = new ConnectionLoggerFactoryImpl();
+ }
+ return loggerFactory;
+ }
+
+ /**
+ * This thing is responsible for creating a separate launch that holds
+ * logger console pseudo-projects.
+ * TODO(peter.rybin): these projects stay as zombies under the launch; fix it
+ */
+ private static class ConnectionLoggerFactoryImpl implements NamedConnectionLoggerFactory {
+ private static final String LAUNCH_CONFIGURATION_FILE_CONTENT = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + //$NON-NLS-1$
+ "<launchConfiguration " + //$NON-NLS-1$
+ "type=\"org.chromium.debug.ui.ConsolePseudoConfigurationType\"/>";
+
+ private final ILaunch commonLaunch;
+
+ /**
+ * Here we create launch configuration and launch, which will hold console pseudo-process.
+ * This is a bit messy, because ILaunchManager mostly supports user creation of new
+ * configurations in UI.
+ */
+ public ConnectionLoggerFactoryImpl() throws CoreException {
+ String location = Messages.LaunchType_LogConsoleLaunchName + "." + //$NON-NLS-1$
+ ILaunchConfiguration.LAUNCH_CONFIGURATION_FILE_EXTENSION;
+ String memento =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + //$NON-NLS-1$
+ "<launchConfiguration " + //$NON-NLS-1$
+ "local=\"true\" path=\"" + location + "\"/>"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ ILaunchConfiguration configuration =
+ DebugPlugin.getDefault().getLaunchManager().getLaunchConfiguration(memento);
+ try {
+ initializeConfigurationFile(configuration.getLocation());
+ } catch (IOException e) {
+ throw new CoreException(new Status(IStatus.ERROR, ChromiumDebugUIPlugin.PLUGIN_ID,
+ "Failed to create launch configuration file", e)); //$NON-NLS-1$
+ }
+ commonLaunch = new Launch(configuration, ILaunchManager.DEBUG_MODE, null);
+ }
+
+ public ConnectionLogger createLogger(String title) {
+ return LaunchTypeBase.createConsoleAndLogger(commonLaunch, true, title);
+ }
+
+ /**
+ * Creates file so that configuration could be read from some location.
+ */
+ private void initializeConfigurationFile(IPath configurationPath) throws IOException {
+ String osPath = configurationPath.toOSString();
+ File file = new File(osPath);
+ synchronized (this) {
+ Writer writer = new OutputStreamWriter(new FileOutputStream(file));
+ writer.write(
+ LAUNCH_CONFIGURATION_FILE_CONTENT); //$NON-NLS-1$
+ writer.close();
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/ChromiumRemoteTab.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,155 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.launcher;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.FieldEditor;
+import org.eclipse.jface.preference.IntegerFieldEditor;
+import org.eclipse.jface.preference.PreferenceStore;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * The "Remote" tab for the Chromium JavaScript launch tab group.
+ */
+public class ChromiumRemoteTab extends AbstractLaunchConfigurationTab {
+
+ private static final String PORT_FIELD_NAME = "port_field"; //$NON-NLS-1$
+ private static final String ADD_NETWORK_CONSOLE_FIELD_NAME =
+ "add_network_console_field"; //$NON-NLS-1$
+
+ // However, recommended range is [1024, 32767].
+ private static final int minimumPortValue = 0;
+ private static final int maximumPortValue = 65535;
+
+ private IntegerFieldEditor debugPort;
+ private BooleanFieldEditor addNetworkConsole;
+ private final PreferenceStore store = new PreferenceStore();
+
+ public void createControl(Composite parent) {
+ Composite composite = createDefaultComposite(parent);
+
+ IPropertyChangeListener modifyListener = new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ updateLaunchConfigurationDialog();
+ }
+ };
+
+ Composite propertiesComp = createInnerComposite(composite, 2);
+ // Port text field
+ debugPort = new IntegerFieldEditor(PORT_FIELD_NAME, Messages.ChromiumRemoteTab_PortLabel,
+ propertiesComp);
+ debugPort.setPropertyChangeListener(modifyListener);
+ debugPort.setPreferenceStore(store);
+
+ addNetworkConsole = new BooleanFieldEditor(ADD_NETWORK_CONSOLE_FIELD_NAME,
+ Messages.ChromiumRemoteTab_ShowDebuggerNetworkCommunication,
+ propertiesComp);
+ addNetworkConsole.setPreferenceStore(store);
+ addNetworkConsole.setPropertyChangeListener(modifyListener);
+ }
+
+ public String getName() {
+ return Messages.ChromiumRemoteTab_RemoteTabName;
+ }
+
+ public void initializeFrom(ILaunchConfiguration config) {
+ int debugPortDefault = PluginVariablesUtil.getValueAsInt(PluginVariablesUtil.DEFAULT_PORT);
+
+ try {
+ store.setDefault(PORT_FIELD_NAME, config.getAttribute(LaunchTypeBase.CHROMIUM_DEBUG_PORT,
+ debugPortDefault));
+ store.setDefault(ADD_NETWORK_CONSOLE_FIELD_NAME, config.getAttribute(
+ LaunchTypeBase.ADD_NETWORK_CONSOLE, false));
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(new Exception("Unexpected storage problem", e)); //$NON-NLS-1$
+ store.setDefault(PORT_FIELD_NAME, debugPortDefault);
+ store.setDefault(ADD_NETWORK_CONSOLE_FIELD_NAME, false);
+ }
+
+ debugPort.loadDefault();
+ addNetworkConsole.loadDefault();
+ }
+
+ public void performApply(ILaunchConfigurationWorkingCopy config) {
+ storeEditor(debugPort, "-1"); //$NON-NLS-1$
+ storeEditor(addNetworkConsole, ""); //$NON-NLS-1$
+
+ config.setAttribute(LaunchTypeBase.CHROMIUM_DEBUG_PORT, store.getInt(PORT_FIELD_NAME));
+ config.setAttribute(LaunchTypeBase.ADD_NETWORK_CONSOLE,
+ store.getBoolean(ADD_NETWORK_CONSOLE_FIELD_NAME));
+ }
+
+ @Override
+ public boolean isValid(ILaunchConfiguration config) {
+ try {
+ int port = config.getAttribute(LaunchTypeBase.CHROMIUM_DEBUG_PORT, -1);
+ if (port < minimumPortValue || port > maximumPortValue) {
+ setErrorMessage(Messages.ChromiumRemoteTab_InvalidPortNumberError);
+ return false;
+ }
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(new Exception("Unexpected storage problem", e)); //$NON-NLS-1$
+ }
+
+ setErrorMessage(null);
+ return true;
+ }
+
+
+ public void setDefaults(ILaunchConfigurationWorkingCopy config) {
+ int port = PluginVariablesUtil.getValueAsInt(PluginVariablesUtil.DEFAULT_PORT);
+ config.setAttribute(LaunchTypeBase.CHROMIUM_DEBUG_PORT, port);
+ }
+
+ @Override
+ public Image getImage() {
+ return DebugUITools.getImage(IDebugUIConstants.IMG_LCL_DISCONNECT);
+ }
+
+ private Composite createDefaultComposite(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NULL);
+ setControl(composite);
+
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 1;
+ composite.setLayout(layout);
+
+ GridData data = new GridData();
+ data.verticalAlignment = GridData.FILL;
+ data.horizontalAlignment = GridData.FILL;
+ composite.setLayoutData(data);
+
+ return composite;
+ }
+
+ private Composite createInnerComposite(Composite parent, int numColumns) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ composite.setLayout(new GridLayout(numColumns, false));
+ GridData gd = new GridData(GridData.FILL_BOTH);
+ composite.setLayoutData(gd);
+ return composite;
+ }
+
+ private static void storeEditor(FieldEditor editor, String errorValue) {
+ if (editor.isValid()) {
+ editor.store();
+ } else {
+ editor.getPreferenceStore().setValue(editor.getPreferenceName(), errorValue);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/LaunchTabGroup.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,26 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.launcher;
+
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
+import org.eclipse.debug.ui.CommonTab;
+import org.eclipse.debug.ui.ILaunchConfigurationDialog;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+
+/**
+ * The Chromium JavaScript debugger launch configuration tab group.
+ */
+public class LaunchTabGroup extends AbstractLaunchConfigurationTabGroup {
+ public static class Chromium extends LaunchTabGroup {
+ }
+
+ public static class StandaloneV8 extends LaunchTabGroup {
+ }
+
+ public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
+ setTabs(new ILaunchConfigurationTab[] { new ChromiumRemoteTab(), new CommonTab() });
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/LaunchTypeBase.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,152 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.launcher;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.chromium.debug.core.model.ConnectionLoggerImpl;
+import org.chromium.debug.core.model.ConsolePseudoProcess;
+import org.chromium.debug.core.model.DebugTargetImpl;
+import org.chromium.debug.core.model.Destructable;
+import org.chromium.debug.core.model.DestructingGuard;
+import org.chromium.debug.core.model.JavascriptVmEmbedder;
+import org.chromium.debug.core.model.NamedConnectionLoggerFactory;
+import org.chromium.debug.ui.PluginUtil;
+import org.chromium.sdk.ConnectionLogger;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.core.model.ILaunchConfigurationDelegate;
+
+/**
+ * A launch configuration delegate for the JavaScript debugging.
+ */
+public abstract class LaunchTypeBase implements ILaunchConfigurationDelegate {
+
+ /** Launch configuration attribute (debug port). */
+ public static final String CHROMIUM_DEBUG_PORT = "debug_port"; //$NON-NLS-1$
+
+ public static final String ADD_NETWORK_CONSOLE = "add_network_console"; //$NON-NLS-1$
+
+ public void launch(ILaunchConfiguration config, String mode, final ILaunch launch,
+ IProgressMonitor monitor) throws CoreException {
+ if (!mode.equals(ILaunchManager.DEBUG_MODE)) {
+ // Chromium JavaScript launch is only supported for debugging.
+ return;
+ }
+
+ int port =
+ config.getAttribute(LaunchTypeBase.CHROMIUM_DEBUG_PORT,
+ PluginVariablesUtil.getValueAsInt(PluginVariablesUtil.DEFAULT_PORT));
+
+ boolean addNetworkConsole = config.getAttribute(LaunchTypeBase.ADD_NETWORK_CONSOLE, false);
+
+ JavascriptVmEmbedder.ConnectionToRemote remoteServer =
+ createConnectionToRemote(port, launch, addNetworkConsole);
+ try {
+
+ String projectNameBase = config.getName();
+
+ DestructingGuard destructingGuard = new DestructingGuard();
+ try {
+ Destructable lauchDestructor = new Destructable() {
+ public void destruct() {
+ if (!launch.hasChildren()) {
+ DebugPlugin.getDefault().getLaunchManager().removeLaunch(launch);
+ }
+ }
+ };
+
+ destructingGuard.addValue(lauchDestructor);
+
+ final DebugTargetImpl target = new DebugTargetImpl(launch);
+
+ Destructable targetDestructor = new Destructable() {
+ public void destruct() {
+ terminateTarget(target);
+ }
+ };
+ destructingGuard.addValue(targetDestructor);
+ boolean attached = target.attach(
+ projectNameBase, remoteServer, destructingGuard,
+ new Runnable() {
+ public void run() {
+ PluginUtil.openProjectExplorerView();
+ }
+ },
+ monitor);
+ if (!attached) {
+ // Error
+ return;
+ }
+
+ launch.setSourceLocator(target.getSourceLocator());
+
+ launch.addDebugTarget(target);
+ monitor.done();
+
+ // All OK
+ destructingGuard.discharge();
+ } finally {
+ destructingGuard.doFinally();
+ }
+
+ } finally {
+ remoteServer.disposeConnection();
+ }
+ }
+
+ protected abstract JavascriptVmEmbedder.ConnectionToRemote createConnectionToRemote(int port,
+ ILaunch launch, boolean addConsoleLogger) throws CoreException;
+
+ private static void terminateTarget(DebugTargetImpl target) {
+ target.setDisconnected(true);
+ target.fireTerminateEvent();
+ }
+
+ static ConnectionLogger createConsoleAndLogger(final ILaunch launch,
+ final boolean addLaunchToManager, final String title) {
+ final ConsolePseudoProcess.Retransmitter consoleRetransmitter =
+ new ConsolePseudoProcess.Retransmitter();
+
+ // This controller is responsible for creating ConsolePseudoProcess only on
+ // logStarted call. Before this ConnectionLoggerImpl with all it fields should stay
+ // garbage-collectible, because connection may not even start.
+ ConnectionLoggerImpl.LogLifecycleListener consoleController =
+ new ConnectionLoggerImpl.LogLifecycleListener() {
+ private final AtomicBoolean alreadyStarted = new AtomicBoolean(false);
+
+ public void logClosed() {
+ consoleRetransmitter.processClosed();
+ }
+
+ public void logStarted(ConnectionLoggerImpl connectionLogger) {
+ boolean res = alreadyStarted.compareAndSet(false, true);
+ if (!res) {
+ throw new IllegalStateException();
+ }
+ ConsolePseudoProcess consolePseudoProcess = new ConsolePseudoProcess(launch, title,
+ consoleRetransmitter, connectionLogger.getConnectionTerminate());
+ consoleRetransmitter.startFlushing();
+ if (addLaunchToManager) {
+ // Active the launch (again if it has already been removed)
+ DebugPlugin.getDefault().getLaunchManager().addLaunch(launch);
+ }
+ }
+ };
+
+ return new ConnectionLoggerImpl(consoleRetransmitter, consoleController);
+ }
+
+ static final NamedConnectionLoggerFactory NO_CONNECTION_LOGGER_FACTORY =
+ new NamedConnectionLoggerFactory() {
+ public ConnectionLogger createLogger(String title) {
+ return null;
+ }
+ };
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/Messages.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,32 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.launcher;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * NLS messages for the package.
+ */
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME =
+ "org.chromium.debug.ui.launcher.messages"; //$NON-NLS-1$
+
+ public static String ChromiumRemoteTab_ShowDebuggerNetworkCommunication;
+
+ public static String ChromiumRemoteTab_PortLabel;
+
+ public static String ChromiumRemoteTab_RemoteTabName;
+
+ public static String ChromiumRemoteTab_InvalidPortNumberError;
+
+ public static String LaunchType_LogConsoleLaunchName;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/PluginVariablesUtil.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,42 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.launcher;
+
+import org.chromium.debug.ui.ChromiumDebugUIPlugin;
+import org.eclipse.core.variables.VariablesPlugin;
+
+/**
+ * Provides convenient access to the variables declared in the
+ * org.eclipse.core.variables.valueVariables extension point.
+ */
+class PluginVariablesUtil {
+
+ /** The default server port variable id. */
+ public static final String DEFAULT_PORT =
+ ChromiumDebugUIPlugin.PLUGIN_ID + ".chromium_debug_port"; //$NON-NLS-1$
+
+ /**
+ * @param variableName to get the value for
+ * @return the variable value parsed as an integer
+ * @throws NumberFormatException
+ * if the value cannot be parsed as an integer
+ */
+ public static int getValueAsInt(String variableName) {
+ return Integer.parseInt(getValue(variableName));
+ }
+
+ /**
+ * @param variableName to get the value for
+ * @return the value of the specified variable
+ */
+ public static String getValue(String variableName) {
+ return VariablesPlugin.getDefault().getStringVariableManager()
+ .getValueVariable(variableName).getValue();
+ }
+
+ private PluginVariablesUtil() {
+ // not instantiable
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/StandaloneV8LaunchType.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,27 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+package org.chromium.debug.ui.launcher;
+
+import org.chromium.debug.core.model.JavascriptVmEmbedder;
+import org.chromium.debug.core.model.JavascriptVmEmbedderFactory;
+import org.chromium.debug.core.model.NamedConnectionLoggerFactory;
+import org.chromium.sdk.ConnectionLogger;
+import org.eclipse.debug.core.ILaunch;
+
+public class StandaloneV8LaunchType extends LaunchTypeBase {
+ @Override
+ protected JavascriptVmEmbedder.ConnectionToRemote createConnectionToRemote(int port,
+ final ILaunch launch, boolean addConsoleLogger) {
+ NamedConnectionLoggerFactory consoleFactory;
+ if (addConsoleLogger) {
+ consoleFactory = new NamedConnectionLoggerFactory() {
+ public ConnectionLogger createLogger(String title) {
+ return LaunchTypeBase.createConsoleAndLogger(launch, false, title);
+ }
+ };
+ } else {
+ consoleFactory = NO_CONNECTION_LOGGER_FACTORY;
+ }
+ return JavascriptVmEmbedderFactory.connectToStandalone(port, consoleFactory);
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/messages.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,6 @@
+ChromiumRemoteTab_ShowDebuggerNetworkCommunication=Show debugger network communication console
+ChromiumRemoteTab_PortLabel=Port:
+ChromiumRemoteTab_RemoteTabName=Remote
+ChromiumRemoteTab_InvalidPortNumberError=Invalid port number
+# note that this string is a part of the hidden filename, so translate with care
+LaunchType_LogConsoleLaunchName=chromedevtools-logger
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/messages.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,9 @@
+# Copyright (c) 2009 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+ChromiumDebugUIPlugin_Error=Error
+ChromiumDebugUIPlugin_Warning=Warning
+ChromiumDebugUIPlugin_Info=Info
+JsWatchExpressionDelegate_BadStackStructureWhileEvaluating=Bad stack structure encountered while evaluating
+JsWatchExpressionDelegate_ErrorEvaluatingExpression=Error evaluating expression
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/propertypages/JsLineBreakpointPage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,259 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.propertypages;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.debug.core.model.ChromiumLineBreakpoint;
+import org.chromium.debug.core.util.ChromiumDebugPluginUtil;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.dialogs.PropertyPage;
+
+/**
+ * A JavaScript line breakpoint property page.
+ */
+public class JsLineBreakpointPage extends PropertyPage {
+
+ private final List<String> errorMessages = new ArrayList<String>(2);
+
+ private Button enabledCheckbox;
+
+ private Button ignoreCountCheckbox;
+
+ private Text ignoreCountText;
+
+ private Button conditionCheckbox;
+
+ private Text conditionText;
+
+ @Override
+ protected Control createContents(Composite parent) {
+ noDefaultAndApplyButton();
+ Composite mainComposite = createComposite(parent, 2, 1);
+ try {
+ createBreakpointDataControls(mainComposite);
+ createInfoControls(mainComposite);
+ createEnabledControls(mainComposite);
+ createIgnoreCountControls(mainComposite);
+ createConditionControls(mainComposite);
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ setValid(true);
+ return mainComposite;
+ }
+
+ @Override
+ public boolean performOk() {
+ IWorkspaceRunnable runnable = new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ storePrefs();
+ }
+ };
+ try {
+ ResourcesPlugin.getWorkspace().run(runnable, null, 0, null);
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ return super.performOk();
+ }
+
+ private void storePrefs() throws CoreException {
+ ChromiumLineBreakpoint breakpoint = getBreakpoint();
+ breakpoint.setEnabled(enabledCheckbox.getSelection());
+ breakpoint.setIgnoreCount(ignoreCountCheckbox.getSelection()
+ ? Integer.valueOf(ignoreCountText.getText())
+ : -1);
+ String condition = null;
+ if (conditionCheckbox.getSelection()) {
+ String text = conditionText.getText().trim();
+ if (text.length() > 0) {
+ condition = text;
+ }
+ }
+ breakpoint.setCondition(condition);
+ }
+
+ private void createBreakpointDataControls(Composite mainComposite) {
+ // new Label
+ }
+
+ private void createInfoControls(Composite parent) {
+ Composite infoComposite = createComposite(parent, 2, 2);
+ Label resourceLabel = new Label(infoComposite, SWT.NONE);
+ resourceLabel.setText(Messages.JsLineBreakpointPage_ResourceLabel);
+ Label resourceNameLabel = new Label(infoComposite, SWT.NONE);
+ resourceNameLabel.setText(getBreakpoint().getMarker().getResource().getName());
+
+ Label lineNumberLabel = new Label(infoComposite, SWT.NONE);
+ lineNumberLabel.setText(Messages.JsLineBreakpointPage_LineNumberLabel);
+ Label lineNumberValueLabel = new Label(infoComposite, SWT.NONE);
+ String lineNumber = Messages.JsLineBreakpointPage_UnknownLineNumber;
+ try {
+ lineNumber = String.valueOf(getBreakpoint().getLineNumber());
+ } catch (CoreException e) {
+ ChromiumDebugPlugin.log(e);
+ }
+ lineNumberValueLabel.setText(lineNumber);
+ }
+
+ private void createEnabledControls(Composite parent) throws CoreException {
+ enabledCheckbox = new Button(parent, SWT.CHECK);
+ GridData gd = new GridData();
+ gd.horizontalSpan = 2;
+ enabledCheckbox.setLayoutData(gd);
+ enabledCheckbox.setSelection(getBreakpoint().isEnabled());
+ enabledCheckbox.setText(Messages.JavascriptLineBreakpointPage_Enabled);
+ }
+
+ private void createIgnoreCountControls(Composite parent) throws CoreException {
+ ignoreCountCheckbox = new Button(parent, SWT.CHECK);
+ Integer ignoreCount = getBreakpoint().getIgnoreCount();
+ ignoreCountCheckbox.setSelection(ignoreCount != null);
+ ignoreCountCheckbox.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ ignoreCountText.setEnabled(ignoreCountCheckbox.getSelection());
+ ignoreCountChanged();
+ }
+ });
+ ignoreCountCheckbox.setText(Messages.JavascriptLineBreakpointPage_IgnoreCount);
+ ignoreCountText = new Text(parent, SWT.SINGLE | SWT.BORDER);
+ ignoreCountText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ ignoreCountText.setTextLimit(10);
+ ignoreCountText.setEnabled(ignoreCountCheckbox.getSelection());
+ if (ignoreCount != null) {
+ ignoreCountText.setText(String.valueOf(ignoreCount));
+ }
+ ignoreCountText.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ ignoreCountChanged();
+ }
+ });
+ }
+
+ private void ignoreCountChanged() {
+ boolean isOn = ignoreCountCheckbox.getSelection();
+ if (!isOn) {
+ removeErrorMessage(Messages.JavascriptLineBreakpointPage_IgnoreCountErrorMessage);
+ return;
+ }
+ String value = ignoreCountText.getText();
+ int ignoreCount = -1;
+ if (!ChromiumDebugPluginUtil.isInteger(value)) {
+ addErrorMessage(Messages.JavascriptLineBreakpointPage_IgnoreCountErrorMessage);
+ return;
+ }
+ ignoreCount = Integer.valueOf(value);
+ if (ignoreCount < 1) {
+ addErrorMessage(Messages.JavascriptLineBreakpointPage_IgnoreCountErrorMessage);
+ } else {
+ removeErrorMessage(Messages.JavascriptLineBreakpointPage_IgnoreCountErrorMessage);
+ }
+ }
+
+ private void addErrorMessage(String message) {
+ errorMessages.remove(message);
+ errorMessages.add(message);
+ setErrorMessage(message);
+ }
+
+ private void removeErrorMessage(String message) {
+ errorMessages.remove(message);
+ if (errorMessages.isEmpty()) {
+ setErrorMessage(null);
+ } else {
+ setErrorMessage(errorMessages.get(errorMessages.size() - 1));
+ }
+ }
+
+ @Override
+ public void setErrorMessage(String newMessage) {
+ super.setErrorMessage(newMessage);
+ setValid(newMessage == null);
+ }
+
+ private void createConditionControls(Composite parent) {
+ conditionCheckbox = new Button(parent, SWT.CHECK);
+ conditionCheckbox.setSelection(getBreakpoint().getCondition() != null);
+ conditionCheckbox.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ conditionText.setEnabled(conditionCheckbox.getSelection());
+ conditionChanged();
+ }
+ });
+ conditionCheckbox.setText(Messages.JavascriptLineBreakpointPage_EnableCondition);
+ GridData gd = new GridData();
+ gd.horizontalSpan = 2;
+ conditionCheckbox.setLayoutData(gd);
+ conditionText = new Text(parent, SWT.MULTI | SWT.BORDER);
+ gd = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1);
+ conditionText.setLayoutData(gd);
+ conditionText.setTextLimit(300);
+ conditionText.setFont(JFaceResources.getTextFont());
+ conditionText.setEnabled(conditionCheckbox.getSelection());
+ conditionText.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ conditionChanged();
+ }
+ });
+ conditionText.setText(maskNull(getBreakpoint().getCondition()));
+ }
+
+ private static String maskNull(String value) {
+ return value == null
+ ? "" : value; //$NON-NLS-1$
+ }
+
+ private void conditionChanged() {
+ boolean isOn = conditionCheckbox.getSelection();
+ if (!isOn) {
+ removeErrorMessage(Messages.JavascriptLineBreakpointPage_BreakpointConditionErrorMessage);
+ return;
+ }
+ String value = conditionText.getText();
+ if (value == null) {
+ addErrorMessage(Messages.JavascriptLineBreakpointPage_BreakpointConditionErrorMessage);
+ } else {
+ removeErrorMessage(Messages.JavascriptLineBreakpointPage_BreakpointConditionErrorMessage);
+ }
+ }
+
+ private Composite createComposite(Composite parent, int columns, int horizontalSpan) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout(columns, false);
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ composite.setLayout(layout);
+ composite.setFont(parent.getFont());
+ GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
+ gridData.horizontalSpan = horizontalSpan;
+ composite.setLayoutData(gridData);
+ return composite;
+ }
+
+ protected ChromiumLineBreakpoint getBreakpoint() {
+ return (ChromiumLineBreakpoint) getElement();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/propertypages/Messages.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,38 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.ui.propertypages;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * NLS messages for the package.
+ */
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME =
+ "org.chromium.debug.ui.propertypages.messages"; //$NON-NLS-1$
+
+ public static String JavascriptLineBreakpointPage_BreakpointConditionErrorMessage;
+
+ public static String JavascriptLineBreakpointPage_EnableCondition;
+
+ public static String JavascriptLineBreakpointPage_Enabled;
+
+ public static String JavascriptLineBreakpointPage_IgnoreCount;
+
+ public static String JavascriptLineBreakpointPage_IgnoreCountErrorMessage;
+
+ public static String JsLineBreakpointPage_LineNumberLabel;
+
+ public static String JsLineBreakpointPage_ResourceLabel;
+
+ public static String JsLineBreakpointPage_UnknownLineNumber;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.ui/src/org/chromium/debug/ui/propertypages/messages.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,8 @@
+JavascriptLineBreakpointPage_BreakpointConditionErrorMessage=Breakpoint condition must not be null
+JavascriptLineBreakpointPage_EnableCondition=Enable Condition
+JavascriptLineBreakpointPage_Enabled=Enabled
+JavascriptLineBreakpointPage_IgnoreCount=Ignore Count
+JavascriptLineBreakpointPage_IgnoreCountErrorMessage=Ignore count must be a positive integer
+JsLineBreakpointPage_LineNumberLabel=Line Number:
+JsLineBreakpointPage_ResourceLabel=Resource:
+JsLineBreakpointPage_UnknownLineNumber=<unknown>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/.classpath Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry exported="true" kind="lib" path="lib/json_simple/json_simple-1.1.jar" sourcepath="org.chromium.sdksrc.zip"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/.project Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.chromium.sdk</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ </natures>
+</projectDescription>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/LICENSE Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 The Chromium Authors. 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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/META-INF/MANIFEST.MF Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,16 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Google Chrome Developer Tools SDK
+Bundle-SymbolicName: org.chromium.sdk
+Bundle-Version: 0.1.3.qualifier
+Bundle-Vendor: The Chromium Authors
+Bundle-ClassPath: lib/json_simple/json_simple-1.1.jar,
+ .
+Export-Package: org.chromium.sdk,
+ org.chromium.sdk.internal;x-internal:=true,
+ org.chromium.sdk.internal.tools.devtools;x-internal:=true,
+ org.chromium.sdk.internal.tools.v8;x-internal:=true,
+ org.chromium.sdk.internal.tools.v8.processor;x-internal:=true,
+ org.chromium.sdk.internal.tools.v8.request;x-internal:=true,
+ org.chromium.sdk.internal.transport;x-internal:=true
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Binary file org.chromium.sdk/bin/org/chromium/sdk/Breakpoint$Type.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/Breakpoint.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/Browser$TabConnector.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/Browser$TabFetcher.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/Browser.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/BrowserFactory.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/BrowserTab.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/CallFrame$EvaluateCallback.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/CallFrame.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/CallbackSemaphore.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/ConnectionLogger$ConnectionCloser.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/ConnectionLogger$Factory.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/ConnectionLogger.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/DebugContext$ContinueCallback.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/DebugContext$State.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/DebugContext$StepAction.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/DebugContext.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/DebugEventListener.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/ExceptionData.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/InvalidContextException.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/JavascriptVm$BreakpointCallback.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/JavascriptVm$ScriptsCallback.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/JavascriptVm$SuspendCallback.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/JavascriptVm.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/JsArray.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/JsFunction.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/JsObject.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/JsScope$Type.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/JsScope.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/JsValue$Type.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/JsValue.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/JsVariable$SetValueCallback.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/JsVariable.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/Script$Type.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/Script.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/StandaloneVm.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/SyncCallback.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/TabDebugEventListener.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/UnsupportedVersionException.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/Version.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserFactoryImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$ConnectionSessionManager.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$ExceptionWrapper$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$ExceptionWrapper$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$ExceptionWrapper.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$Session$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$Session$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$Session.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$TabConnectorImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$TabFetcherImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserTabImpl$ChromeDevToolOutput.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserTabImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/CallFrameImpl$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/CallFrameImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/CloseableMap$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/CloseableMap.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ConnectionFactory.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$1$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$DebugContextData.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$ExpectingBacktraceStep.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$ExpectingBreakEventStep.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$Frames.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$PreContext$UserContext$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$PreContext$UserContext.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$PreContext.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/DataWithRef$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/DataWithRef$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/DataWithRef.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/DebugSession$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/DebugSession$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/DebugSession$ScriptLoader$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/DebugSession$ScriptLoader.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/DebugSession.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/DebugSessionManager.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ExceptionDataImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/FrameMirror.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/FunctionAdditionalProperties.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/HandleManager.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/InternalContext$ContextDismissedCheckedException.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/InternalContext.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JavascriptVmImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsArrayImpl$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsArrayImpl$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsArrayImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsDataTypeUtil.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsFunctionImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsObjectImpl$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsObjectImpl$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsObjectImpl$Subproperties.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsObjectImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsScopeImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsValueImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsVariableImpl$NameDecorator$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsVariableImpl$NameDecorator.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsVariableImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsonException.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/JsonUtil.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/MessageFactory.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/PropertyHoldingValueMirror.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/PropertyReference.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/PropertyType.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/Result.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ScopeMirror.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ScriptImpl$Descriptor.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ScriptImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ScriptManager$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ScriptManager$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ScriptManager$Callback.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ScriptManager.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/SessionManager$SessionBase$TicketImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/SessionManager$SessionBase.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/SessionManager$Ticket.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/SessionManager.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/SocketConnectionFactory.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/StandaloneVmImpl$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/StandaloneVmImpl$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/StandaloneVmImpl$3.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/StandaloneVmImpl$4.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/StandaloneVmImpl$ConnectionState.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/StandaloneVmImpl$V8CommandOutputImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/StandaloneVmImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror$FunctionValueBased.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror$JsonBased$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror$JsonBased$AdditionalPropertyFactory.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror$JsonBased.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror$ListBased.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror$ObjectValueBased.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/V8ContextFilter.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/V8VersionMilestones.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ValueLoadException.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ValueLoader$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ValueLoader$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ValueLoader.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/ValueMirror.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/AfterCompileBody.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/BacktraceCommandBody.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/BreakEventBody.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/BreakpointBody.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/CommandResponse$TypeValueCondition.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/CommandResponse.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/CommandResponseBody.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/EventNotification$TypeValueCondition.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/EventNotification.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/EventNotificationBody.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/FailedCommandResponse.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/FrameObject.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/IncomingMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/MessageType.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/ScopeBody.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/ScopeRef.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/SuccessCommandResponse.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/VersionBody.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/ContextData.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/ContextHandle.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/FunctionValueHandle.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/ObjectValueHandle.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/PropertyObject.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/PropertyWithRef.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/PropertyWithValue.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/RefWithDisplayData.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/ScriptHandle.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/SomeHandle.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/SomeRef.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/SomeSerialized.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/ValueHandle.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/AnyObjectBased.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/EnumValueCondition.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonField.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonNullable.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonObjectBased.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonOptionalField.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonOverrideField.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonProtocolModelParseException.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonProtocolParseException.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonSubtype.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonSubtypeCasting.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonSubtypeCondition.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonSubtypeConditionBoolValue.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonSubtypeConditionCustom.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonType.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonValueCondition.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/BaseHandlersLibrary$GetAnyObjectMethodHaldler.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/BaseHandlersLibrary$GetJsonObjectMethodHaldler.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/BaseHandlersLibrary$GetSuperMethodHaldler.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/BaseHandlersLibrary$MethodHandlerBase.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/BaseHandlersLibrary$SelfCallMethodHanlder.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/BaseHandlersLibrary.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/EnumParser.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldCondition.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic$3.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic$4.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic$5.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic$CustomConditionWrapper.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldLoadedFinisher.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldLoader.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonInvocationHandler.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$AlgebraicCasesDataImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$ArrayParser.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$AutoSubtypeMethodHandler.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$EagerFieldParserImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$FieldMap.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$JsonProtocolParseRuntimeException.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$LazyParseFieldMethodHandler.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$ManualSubtypeMethodHandler.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$PreparsedFieldMethodHandler.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$ReadInterfacesSession$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$ReadInterfacesSession$FieldProcessor$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$ReadInterfacesSession$FieldProcessor$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$ReadInterfacesSession$FieldProcessor.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$ReadInterfacesSession.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$RefImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$SimpleCastParser.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$SimpleParserPair.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonTypeParser$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonTypeParser.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/MethodHandler.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/ObjectData.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/QuickParser.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/RefToType.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/SlowParser.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/SubtypeCaster.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler$AbsentSubtypeAspect.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler$AlgebraicCasesData.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler$EagerFieldParser.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler$ExistingSubtypeAspect.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler$SubtypeAspect.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler$SubtypeSupport.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/ChromeDevToolsProtocol.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/ToolHandler.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/ToolName.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/ToolOutput.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceCommand.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler$CommandFactory.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler$ListTabsCallback.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler$TabIdAndUrl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler$VersionCallback.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/BreakpointImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/BreakpointManager$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/BreakpointManager$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/BreakpointManager$3.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/BreakpointManager.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$AttachState.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$AttachmentFailureException.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$ResultAwareCallback.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$ToolHandlerImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$V8CommandOutputImpl.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$V8DebuggerToolMessageFactory.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/DebuggerCommand.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/DebuggerToolCommand.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/DefaultResponseHandler$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/DefaultResponseHandler$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/DefaultResponseHandler$ProcessorGetter.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/DefaultResponseHandler.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/MethodIsBlockingException.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8BlockingCallback.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandOutput.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandProcessor$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandProcessor$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandProcessor$CallbackCaller.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandProcessor$CallbackEntry.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandProcessor$V8HandlerCallback$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandProcessor$V8HandlerCallback.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandProcessor.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandSender.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8Helper$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8Helper$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8Helper$3.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8Helper$CallbackException.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8Helper.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8Protocol.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil$PropertyNameGetter$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil$PropertyNameGetter$SimpleNameGetter.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil$PropertyNameGetter$SubpropertyNameGetter.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil$PropertyNameGetter.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/processor/AfterCompileProcessor$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/processor/AfterCompileProcessor.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/processor/BacktraceProcessor$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/processor/BacktraceProcessor.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/processor/BreakpointProcessor.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/processor/V8EventProcessor.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/BacktraceMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/ChangeBreakpointMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/ClearBreakpointMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/ContextlessDebuggerMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/ContinueMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/DebuggerMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/DebuggerMessageFactory.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/EvaluateMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/FrameMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/LookupMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/ScopeMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/ScriptsMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/SeqGenerator.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/SetBreakpointMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/SourceMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/SuspendMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/V8MessageType.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/VersionMessage.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Connection$NetListener.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Connection.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Handshaker$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Handshaker$StandaloneV8$HandshakeTaks$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Handshaker$StandaloneV8$HandshakeTaks.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Handshaker$StandaloneV8$RemoteInfo.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Handshaker$StandaloneV8.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Handshaker.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Message$Header.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Message$MalformedMessageException.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Message.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$1.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$2.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$3.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$4.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$InterruptibleThread.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$MessageItem.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$ReaderThread.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$RegularMessageItem.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$ResponseDispatcherThread.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$WriterThread.class has changed
Binary file org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection.class has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/build.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,8 @@
+bin.includes = META-INF/,\
+ .,\
+ lib/,\
+ LICENSE
+jars.compile.order = .
+source.. = src/
+output.. = bin/
+src.includes = LICENSE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/lib/json_simple/AUTHORS.txt Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,1 @@
+Fang Yidong <fangyidong@yahoo.com.cn>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/lib/json_simple/LICENSE.txt Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,162 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction,
+and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the
+copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other
+entities that control, are controlled by, or are under common control
+with that entity. For the purposes of this definition, "control" means
+(i) the power, direct or indirect, to cause the direction or management
+of such entity, whether by contract or otherwise, or (ii) ownership of
+fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
+ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications,
+including but not limited to software source code, documentation source,
+and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation
+or translation of a Source form, including but not limited to compiled object
+code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form,
+made available under the License, as indicated by a copyright notice that is
+included in or attached to the work (an example is provided in the Appendix
+below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form,
+that is based on (or derived from) the Work and for which the editorial
+revisions, annotations, elaborations, or other modifications represent,
+as a whole, an original work of authorship. For the purposes of this License,
+Derivative Works shall not include works that remain separable from, or merely
+link (or bind by name) to the interfaces of, the Work and Derivative Works
+thereof.
+
+"Contribution" shall mean any work of authorship, including the original
+version of the Work and any modifications or additions to that Work or
+Derivative Works thereof, that is intentionally submitted to Licensor for
+inclusion in the Work by the copyright owner or by an individual or Legal
+Entity authorized to submit on behalf of the copyright owner. For the purposes
+of this definition, "submitted" means any form of electronic, verbal, or
+written communication sent to the Licensor or its representatives, including
+but not limited to communication on electronic mailing lists, source code
+control systems, and issue tracking systems that are managed by, or on behalf
+of, the Licensor for the purpose of discussing and improving the Work, but
+excluding communication that is conspicuously marked or otherwise designated
+in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this
+License, each Contributor hereby grants to You a perpetual, worldwide,
+non-exclusive, no-charge, royalty-free, irrevocable copyright license to
+reproduce, prepare Derivative Works of, publicly display, publicly perform,
+sublicense, and distribute the Work and such Derivative Works in Source or
+Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this
+License, each Contributor hereby grants to You a perpetual, worldwide,
+non-exclusive, no-charge, royalty-free, irrevocable (except as stated in
+this section) patent license to make, have made, use, offer to sell, sell,
+import, and otherwise transfer the Work, where such license applies only
+to those patent claims licensable by such Contributor that are necessarily
+infringed by their Contribution(s) alone or by combination of their
+Contribution(s) with the Work to which such Contribution(s) was submitted.
+If You institute patent litigation against any entity (including a cross-claim
+or counterclaim in a lawsuit) alleging that the Work or a Contribution
+incorporated within the Work constitutes direct or contributory patent
+infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or
+Derivative Works thereof in any medium, with or without modifications,
+and in Source or Object form, provided that You meet the following conditions:
+
+ 1. You must give any other recipients of the Work or Derivative Works a copy
+of this License; and
+
+ 2. You must cause any modified files to carry prominent notices stating that
+You changed the files; and
+
+ 3. You must retain, in the Source form of any Derivative Works that You
+distribute, all copyright, patent, trademark, and attribution notices from
+the Source form of the Work, excluding those notices that do not pertain to
+any part of the Derivative Works; and
+
+ 4. If the Work includes a "NOTICE" text file as part of its distribution,
+then any Derivative Works that You distribute must include a readable copy
+of the attribution notices contained within such NOTICE file, excluding those
+notices that do not pertain to any part of the Derivative Works, in at least
+one of the following places: within a NOTICE text file distributed as part of
+the Derivative Works; within the Source form or documentation, if provided
+along with the Derivative Works; or, within a display generated by the
+Derivative Works, if and wherever such third-party notices normally appear.
+The contents of the NOTICE file are for informational purposes only and do
+not modify the License. You may add Your own attribution notices within
+Derivative Works that You distribute, alongside or as an addendum to the
+NOTICE text from the Work, provided that such additional attribution notices
+cannot be construed as modifying the License.
+
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction,
+or distribution of Your modifications, or for any such Derivative Works as a
+whole, provided Your use, reproduction, and distribution of the Work otherwise
+complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+any Contribution intentionally submitted for inclusion in the Work by You
+to the Licensor shall be under the terms and conditions of this License,
+without any additional terms or conditions. Notwithstanding the above,
+nothing herein shall supersede or modify the terms of any separate license
+agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names,
+trademarks, service marks, or product names of the Licensor, except as
+required for reasonable and customary use in describing the origin of the
+Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to
+in writing, Licensor provides the Work (and each Contributor provides its
+Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied, including, without limitation, any warranties
+or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+PARTICULAR PURPOSE. You are solely responsible for determining the
+appropriateness of using or redistributing the Work and assume any risks
+associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether
+in tort (including negligence), contract, or otherwise, unless required by
+applicable law (such as deliberate and grossly negligent acts) or agreed to
+in writing, shall any Contributor be liable to You for damages, including
+any direct, indirect, special, incidental, or consequential damages of any
+character arising as a result of this License or out of the use or inability
+to use the Work (including but not limited to damages for loss of goodwill,
+work stoppage, computer failure or malfunction, or any and all other
+commercial damages or losses), even if such Contributor has been advised of
+the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work
+or Derivative Works thereof, You may choose to offer, and charge a fee for,
+acceptance of support, warranty, indemnity, or other liability obligations
+and/or rights consistent with this License. However, in accepting such
+obligations, You may act only on Your own behalf and on Your sole
+responsibility, not on behalf of any other Contributor, and only if You agree
+to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/lib/json_simple/README.txt Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,144 @@
+Simple Java toolkit for JSON (JSON.simple)
+==========================================
+
+1.Why the Simple Java toolkit (also named as JSON.simple) for JSON?
+
+ When I use JSON as the data exchange format between the AJAX client and JSP
+ for the first time, what worry me mostly is how to encode Java strings and
+ numbers correctly in the server side so the AJAX client will receive a well
+ formed JSON data. When I looked into the 'JSON in Java' directory in JSON
+ website,I found that wrappers to JSONObject and JSONArray can be simpler,
+ due to the simplicity of JSON itself. So I wrote the JSON.simple package.
+
+2.Is it simple,really?
+
+ I think so. Take an example:
+
+ import org.json.simple.JSONObject;
+
+ JSONObject obj=new JSONObject();
+ obj.put("name","foo");
+ obj.put("num",new Integer(100));
+ obj.put("balance",new Double(1000.21));
+ obj.put("is_vip",new Boolean(true));
+ obj.put("nickname",null);
+ System.out.print(obj);
+
+ Result:
+ {"nickname":null,"num":100,"balance":1000.21,"is_vip":true,"name":"foo"}
+
+ The JSONObject.toString() will escape controls and specials correctly.
+
+3.How to use JSON.simple in JSP?
+
+ Take an example in JSP:
+
+ <%@page contentType="text/html; charset=UTF-8"%>
+ <%@page import="org.json.simple.JSONObject"%>
+ <%
+ JSONObject obj=new JSONObject();
+ obj.put("name","foo");
+ obj.put("num",new Integer(100));
+ obj.put("balance",new Double(1000.21));
+ obj.put("is_vip",new Boolean(true));
+ obj.put("nickname",null);
+ out.print(obj);
+ out.flush();
+ %>
+
+ So the AJAX client will get the responseText.
+
+4.Some details about JSONObject?
+
+ JSONObject inherits java.util.HashMap,so it don't have to worry about the
+ mapping things between keys and values. Feel free to use the Map methods
+ like get(), put(), and remove() and others. JSONObject.toString() will
+ combine key value pairs to get the JSON data string. Values will be escaped
+ into JSON quote string format if it's an instance of java.lang.String. Other
+ type of instance like java.lang.Number,java.lang.Boolean,null,JSONObject and
+ JSONArray will NOT escape, just take their java.lang.String.valueOf() result.
+ null value will be the JSON 'null' in the result.
+
+ It's still correct if you put an instance of JSONObject or JSONArray into an
+ instance of JSONObject or JSONArray. Take the example about:
+
+ JSONObject obj2=new JSONObject();
+ obj2.put("phone","123456");
+ obj2.put("zip","7890");
+ obj.put("contact",obj2);
+ System.out.print(obj);
+
+ Result:
+ {"nickname":null,"num":100,"contact":{"phone":"123456","zip":"7890"},"balance":1000.21,"is_vip":true,"name":"foo"}
+
+ The method JSONObject.escape() is used to escape Java string into JSON quote
+ string. Controls and specials will be escaped correctly into \b,\f,\r,\n,\t,
+ \",\\,\/,\uhhhh.
+
+5.Some detail about JSONArray?
+
+ org.json.simple.JSONArray inherits java.util.ArrayList. Feel free to use the
+ List methods like get(),add(),remove(),iterator() and so on. The rules of
+ JSONArray.toString() is similar to JSONObject.toString(). Here's the example:
+
+ import org.json.simple.JSONArray;
+
+ JSONArray array=new JSONArray();
+ array.add("hello");
+ array.add(new Integer(123));
+ array.add(new Boolean(false));
+ array.add(null);
+ array.add(new Double(123.45));
+ array.add(obj2);//see above
+ System.out.print(array);
+
+ Result:
+ ["hello",123,false,null,123.45,{"phone":"123456","zip":"7890"}]
+
+6.What is JSONValue for?
+
+ org.json.simple.JSONValue is use to parse JSON data into Java Object.
+ In JSON, the topmost entity is JSON value, not the JSON object. But
+ it's not necessary to wrap JSON string,boolean,number and null again,
+ for the Java has already had the according classes: java.lang.String,
+ java.lang.Boolean,java.lang.Number and null. The mapping is:
+
+ JSON Java
+ ------------------------------------------------
+ string <=> java.lang.String
+ number <=> java.lang.Number
+ true|false <=> java.lang.Boolean
+ null <=> null
+ array <=> org.json.simple.JSONArray
+ object <=> org.json.simple.JSONObject
+ ------------------------------------------------
+
+ JSONValue has only one kind of method, JSONValue.parse(), which receives
+ a java.io.Reader or java.lang.String. Return type of JSONValue.parse()
+ is according to the mapping above. If the input is incorrect in syntax or
+ there's exceptions during the parsing, I choose to return null, ignoring
+ the exception: I have no idea if it's a serious implementaion, but I think
+ it's convenient to the user.
+
+ Here's the example:
+
+ String s="[0,{\"1\":{\"2\":{\"3\":{\"4\":[5,{\"6\":7}]}}}}]";
+ Object obj=JSONValue.parse(s);
+ JSONArray array=(JSONArray)obj;
+ System.out.println(array.get(1));
+ JSONObject obj2=(JSONObject)array.get(1);
+ System.out.println(obj2.get("1"));
+
+ Result:
+ {"1":{"2":{"3":{"4":[5,{"6":7}]}}}}
+ {"2":{"3":{"4":[5,{"6":7}]}}}
+
+7.About the author.
+
+ I'm a Java EE developer on Linux.
+ I'm working on web systems and information retrieval systems.
+ I also develop 3D games and Flash games.
+
+ You can contact me through:
+ Fang Yidong<fangyidong@yahoo.com.cn>
+ Fang Yidong<fangyidng@gmail.com>
Binary file org.chromium.sdk/lib/json_simple/json_simple-1.1.jar has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/Breakpoint.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,101 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+/**
+ * A breakpoint in the browser JavaScript virtual machine. The {@code set*}
+ * method invocations will not take effect until
+ * {@link #flush(org.chromium.sdk.BrowserTab.BreakpointCallback)} is called.
+ */
+public interface Breakpoint {
+
+ /**
+ * Known breakpoint types.
+ */
+ enum Type {
+ FUNCTION,
+ SCRIPT_NAME,
+ SCRIPT_ID
+ }
+
+ /**
+ * This value is used when the corresponding parameter is absent.
+ *
+ * @see #getIgnoreCount()
+ * @see #setIgnoreCount(int)
+ * @see BrowserTab#setBreakpoint(Type, String, int, int, boolean, String, int, org.chromium.sdk.BrowserTab.BreakpointCallback)
+ */
+ int EMPTY_VALUE = -1;
+
+ /**
+ * A breakpoint has this ID if it does not reflect an actual breakpoint in a
+ * JavaScript VM debugger.
+ */
+ long INVALID_ID = -1;
+
+ /**
+ * @return the breakpoint type
+ */
+ Type getType();
+
+ /**
+ * @return the breakpoint ID as reported by the JavaScript VM debugger
+ */
+ long getId();
+
+ /**
+ * @return whether this breakpoint is enabled
+ */
+ boolean isEnabled();
+
+ /**
+ * Sets whether this breakpoint is enabled.
+ *
+ * @param enabled whether the breakpoint should be enabled
+ */
+ void setEnabled(boolean enabled);
+
+ /**
+ * @return ignore count for this breakpoint or {@code EMPTY_VALUE} if none
+ */
+ int getIgnoreCount();
+
+ /**
+ * Sets the ignore count for this breakpoint ({@code EMPTY_VALUE} to clear).
+ *
+ * @param ignoreCount the new ignored hits count to set
+ */
+ void setIgnoreCount(int ignoreCount);
+
+ /**
+ * @return breakpoint condition as plain JavaScript or {@code null} if none
+ */
+ String getCondition();
+
+ /**
+ * Sets the breakpoint condition as plain JavaScript ({@code null} to clear).
+ *
+ * @param condition the new breakpoint condition
+ */
+ void setCondition(String condition);
+
+ /**
+ * Removes the breakpoint from the JS debugger and invokes the
+ * callback once the operation has finished. This operation does not require
+ * a flush() invocation.
+ *
+ * @param callback to invoke once the operation result is available
+ */
+ void clear(BrowserTab.BreakpointCallback callback);
+
+ /**
+ * Flushes the breakpoint parameter changes (set* methods) into the browser
+ * and invokes the callback once the operation has finished. This method must
+ * be called for the set* method invocations to take effect.
+ *
+ * @param callback to invoke once the operation result is available
+ */
+ void flush(BrowserTab.BreakpointCallback callback);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/Browser.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,79 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * An "entry point" of the SDK. A Browser instance is usually constructed once
+ * per a debugged browser instance.
+ */
+public interface Browser {
+
+ /**
+ * Establishes the browser connection and checks for the protocol version
+ * supported by the remote, then creates object that downloads list of tabs.
+ *
+ * @return new instance of TabFetcher that must be dismissed after use to control
+ * connection use
+ * @throws IOException if there was a transport layer error
+ * @throws UnsupportedVersionException if the SDK protocol version is not
+ * compatible with that supported by the browser
+ */
+ TabFetcher createTabFetcher() throws IOException, UnsupportedVersionException;
+
+
+ /**
+ * Helps to fetch currently opened browser tabs. It also holds open connection to
+ * browser. After instance was used {@code #dismiss} should be called to release
+ * connection. {@link TabConnector#isAlreadyAttached()} helps to tell which
+ * tabs are available for connection.
+ */
+ interface TabFetcher {
+ /**
+ * Retrieves all browser tabs currently opened. It lists all tabs, including
+ * those already attached.
+ *
+ * @return tabs that can be debugged in the associated Browser instance. An
+ * empty list is returned if no tabs are available.
+ * @throws IOException if there was a transport layer failure
+ * @throws IllegalStateException if this method is called while another
+ * invocation of the same method is in flight, or the Browser instance
+ * is not connected
+ */
+ List<? extends TabConnector> getTabs() throws IOException, IllegalStateException;
+
+ /**
+ * Should release connection. If no browser tabs is attached at the moment,
+ * connection may actually close.
+ */
+ void dismiss();
+ }
+
+ /**
+ * Tab list item that is fetched from browser. Connector may correspond to a tab,
+ * which is already attached. Connector is used to attach to tab.
+ */
+ interface TabConnector {
+ /**
+ * @return tab url that should be shown to user to let him select one tab from list
+ */
+ String getUrl();
+
+ /**
+ * @return true if the tab is already attached at this moment
+ */
+ boolean isAlreadyAttached();
+
+ /**
+ * Attaches to the related tab debugger.
+ *
+ * @param listener to report the debug events to
+ * @return null if operation failed
+ */
+ BrowserTab attach(TabDebugEventListener listener) throws IOException;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/BrowserFactory.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,54 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+import java.net.SocketAddress;
+
+import org.chromium.sdk.internal.BrowserFactoryImpl;
+
+/**
+ * A factory for Browser instances.
+ */
+public abstract class BrowserFactory {
+
+ private static BrowserFactory instance;
+
+ /**
+ * Gets a {@link BrowserFactory} instance. This method should be overridden by
+ * implementations that want to construct other implementations of
+ * {@link Browser}.
+ *
+ * @return a BrowserFactory singleton instance
+ */
+ public static BrowserFactory getInstance() {
+ if (instance == null) {
+ instance = new BrowserFactoryImpl();
+ }
+ return instance;
+ }
+
+ /**
+ * Returns a Browser implementor instance that talks to a browser listening at
+ * {@code socketAddress}. Note that you shouldn't try to create several instances
+ * of Browser connecting to the same {@code socketAddress}.
+ *
+ * @param socketAddress the browser is listening on
+ * @param connectionLoggerFactory provides facility for listening to network
+ * traffic; may be null
+ * @return a Browser instance for the {@code socketAddress}
+ */
+ public abstract Browser create(SocketAddress socketAddress,
+ ConnectionLogger.Factory connectionLoggerFactory);
+
+ /**
+ * Constructs StandaloneVm instance that talks to a V8 JavaScript VM via
+ * DebuggerAgent opened at {@code socketAddress}.
+ * @param socketAddress V8 DebuggerAgent is listening on
+ * @param connectionLogger provides facility for listening to network
+ * traffic; may be null
+ */
+ public abstract StandaloneVm createStandalone(SocketAddress socketAddress,
+ ConnectionLogger connectionLogger);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/BrowserTab.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,24 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+
+/**
+ * A lightweight abstraction of a remote Browser tab. Each browser tab
+ * corresponds to a Javascript Virtual Machine and is_a {code JavascriptVm}.
+ */
+public interface BrowserTab extends JavascriptVm {
+
+ /**
+ * @return the "parent" Browser instance
+ */
+ Browser getBrowser();
+
+ /**
+ * @return a URL of the corresponding browser tab
+ */
+ String getUrl();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/CallFrame.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,100 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException;
+
+
+/**
+ * An object that represents a browser JavaScript VM call frame.
+ */
+public interface CallFrame {
+
+ /**
+ * A callback for the "evaluate" request.
+ */
+ interface EvaluateCallback {
+ void success(JsVariable variable);
+
+ void failure(String errorMessage);
+ }
+
+ /**
+ * @return the variables known in this frame, including the receiver variable
+ * @deprecated in favor of {@link #getVariableScopes()}
+ */
+ @Deprecated
+ Collection<? extends JsVariable> getVariables();
+
+ /**
+ * @return the scopes known in this frame; ordered, innermost first, global scope last
+ */
+ List<? extends JsScope> getVariableScopes();
+
+ /**
+ * @return the receiver variable known in this frame
+ */
+ JsVariable getReceiverVariable();
+
+ /**
+ * @return the current line number in the Script corresponding to this frame
+ * (0-based) or {@code -1} if unknown
+ */
+ int getLineNumber();
+
+ /**
+ * @return the start character position in the line corresponding to the
+ * current statement of this frame or {@code -1} if unknown
+ */
+ int getCharStart();
+
+ /**
+ * @return the end character position in the line corresponding to the current
+ * statement of this frame or {@code -1} if unknown
+ */
+ int getCharEnd();
+
+ /**
+ * @return the source script this call frame is associated with. {@code null}
+ * if no script is associated with the call frame (e.g. an exception
+ * could have been thrown in a native script)
+ */
+ Script getScript();
+
+ /**
+ * @return the name of the current function of this frame
+ */
+ String getFunctionName();
+
+ /**
+ * Synchronously evaluates an arbitrary JavaScript {@code expression} in
+ * the context of the call frame. The evaluation result is reported to
+ * the specified {@code evaluateCallback}. The method will block until the evaluation
+ * result is available.
+ *
+ * @param expression to evaluate
+ * @param evaluateCallback to report the evaluation result to
+ * @throws MethodIsBlockingException if called from a callback because it blocks
+ * until remote VM returns result
+ */
+ void evaluateSync(String expression, EvaluateCallback evaluateCallback)
+ throws MethodIsBlockingException;
+
+ /**
+ * Asynchronously evaluates an arbitrary JavaScript {@code expression} in
+ * the context of the call frame. The evaluation result is reported to
+ * the specified {@code evaluateCallback} and right after this to syncCallback.
+ * The method doesn't block.
+ *
+ * @param expression to evaluate
+ * @param evaluateCallback to report the evaluation result to
+ * @param syncCallback to report the end of any processing
+ */
+ void evaluateAsync(String expression, EvaluateCallback evaluateCallback,
+ SyncCallback syncCallback);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/CallbackSemaphore.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,69 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException;
+
+/**
+ * Convenient implementation of {@code SyncCallback}. Client may create one,
+ * then call asynchronous command, and finally wait on blocking method
+ * {@code #tryAcquire()}.
+ */
+public class CallbackSemaphore implements SyncCallback {
+ public static final long OPERATION_TIMEOUT_MS = 120000;
+
+ private final Semaphore sem = new Semaphore(0);
+ private Exception savedException;
+
+ /**
+ * Tries to acquire semaphore with some reasonable default timeout.
+ * @return false if {@code #OPERATION_TIMEOUT_MS} was exceeded and we gave up
+ * @throws MethodIsBlockingException if called from a callback
+ */
+ public boolean tryAcquireDefault() throws MethodIsBlockingException {
+ return tryAcquire(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Tries to acquire the semaphore. This method blocks until the semaphore is
+ * released; typically release call comes from a worker thread of
+ * org.chromium.sdk, the same thread that may call all other callbacks.
+ * It is vital not to call this method from any callback of org.chromium.sdk,
+ * because it's a sure deadlock.
+ * To prevent, this the method declares throwing
+ * {@code MethodIsBlockingException} which is symbolically thrown whenever
+ * someone violates this rule (i.e. invokes this method from a callback).
+ * Though currently nobody actually throws it, such declarations help to
+ * track blocking methods.
+ * @return false if {@code timeout} was exceeded and we gave up
+ * @throws MethodIsBlockingException if called from a callback
+ */
+ public boolean tryAcquire(long timeout, TimeUnit unit) throws MethodIsBlockingException {
+ boolean res;
+ try {
+ res = sem.tryAcquire(timeout, unit);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ if (savedException != null) {
+ throw new RuntimeException("Exception occured in callback", savedException);
+ }
+ return res;
+ }
+ /**
+ * Implementation of {@code SyncCallback#callbackDone(RuntimeException)}.
+ */
+ public void callbackDone(RuntimeException e) {
+ if (e == null) {
+ savedException = null;
+ } else {
+ savedException = new Exception("Exception saved from callback", e);
+ }
+ sem.release();
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/ConnectionLogger.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,68 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+import java.io.Reader;
+import java.io.Writer;
+
+/**
+ * Logger facility for the Chromium debugger connection. It can eavesdrop both
+ * incoming and outgoing streams and log them somewhere. To make other code
+ * less dependent on this interface, it works by wrapping reader/writer and is
+ * only visible at start-up time. This approach has its disadvantages, because
+ * it works with raw data streams, which are not perfectly formatted for human
+ * reading. E.g. adjacent requests or responses are not separated even
+ * by EOL.
+ */
+public interface ConnectionLogger {
+ /**
+ * @return new writer that should pass all data to {@code streamWriter} and
+ * silently copy it elsewhere (without additional exceptions).
+ */
+ Writer wrapWriter(Writer streamWriter);
+
+ /**
+ * @return new reader that should give access to all data
+ * from {@code streamReader} and silently copy it elsewhere (without
+ * additional exceptions).
+ */
+ Reader wrapReader(Reader streamReader);
+
+ /**
+ * Connection may allow the logger to close it. It is nice for UI, where
+ * user sees logger and the corresponding stop button.
+ * TODO(peter.rybin): consider removing it out of logging.
+ */
+ void setConnectionCloser(ConnectionCloser connectionCloser);
+
+ /**
+ * Interface that gives you control over underlying connection.
+ */
+ interface ConnectionCloser {
+ void closeConnection();
+ }
+
+ /**
+ * Notifies logger that actual transmission is starting. After this {@link #handleEos()}
+ * is guaranteed to be called.
+ */
+ void start();
+
+ /**
+ * Notifies logger that EOS has been received from remote. Technically some
+ * traffic still may go through writer (i.e. be sent to remote) after this.
+ */
+ void handleEos();
+
+ /**
+ * Factory for connection logger. ConnectionLogger is NOT reconnectable.
+ */
+ interface Factory {
+ /**
+ * Creates new instance of {@link ConnectionLogger}.
+ */
+ ConnectionLogger newConnectionLogger();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/DebugContext.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,106 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException;
+
+/**
+ * An object that matches the execution state of the browser JavaScript VM while
+ * suspended. It reconstructs and provides access to the current state of the
+ * JavaScript VM.
+ */
+public interface DebugContext {
+
+ /**
+ * JavaScript debugger step actions.
+ */
+ public enum StepAction {
+ /**
+ * Resume the JavaScript execution.
+ */
+ CONTINUE,
+
+ /**
+ * Step into the current statement.
+ */
+ IN,
+
+ /**
+ * Step over the current statement.
+ */
+ OVER,
+
+ /**
+ * Step out of the current function.
+ */
+ OUT
+ }
+
+ /**
+ * The suspension state.
+ */
+ public enum State {
+ /**
+ * A normal suspension (a step end or a breakpoint).
+ */
+ NORMAL,
+
+ /**
+ * A suspension due to an exception.
+ */
+ EXCEPTION
+ }
+
+ /**
+ * A callback for the "continue" request.
+ */
+ interface ContinueCallback {
+ void success();
+
+ void failure(String errorMessage);
+ }
+
+ /**
+ * @return the JavaScript VM suspension state
+ */
+ State getState();
+
+ /**
+ * @return the current exception state, or {@code null} if current state is
+ * not {@code EXCEPTION}
+ * @see #getState()
+ */
+ ExceptionData getExceptionData();
+
+ /**
+ * @return a list of call frames for the current JavaScript suspended state
+ * @throws MethodIsBlockingException if called from a callback because it may
+ * need to load necessary scripts
+ */
+ List<? extends CallFrame> getCallFrames();
+
+ /**
+ * @return a set of the breakpoints hit on VM suspension with which this
+ * context is associated. An empty collection if the suspension was
+ * not related to hitting breakpoints (e.g. a step end)
+ */
+ Collection<? extends Breakpoint> getBreakpointsHit();
+
+ /**
+ * Resumes the JavaScript VM execution using a "continue" request. This
+ * context becomes invalid until another context is supplied through the
+ * {@link DebugEventListener#suspended(DebugContext)} event.
+ *
+ * @param stepAction to perform
+ * @param stepCount steps to perform (not used if
+ * {@code stepAction == CONTINUE})
+ * @param callback to invoke when the request result is ready
+ */
+ void continueVm(StepAction stepAction, int stepCount, ContinueCallback callback);
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/DebugEventListener.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,42 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+/**
+ * This interface is used by the SDK to report debug events for a certain {@link JavascriptVm} to
+ * the clients.
+ */
+public interface DebugEventListener {
+
+ /**
+ * Reports the browser JavaScript virtual machine has suspended (on hitting
+ * breakpoints or a step end). The {@code context} can be used to access the
+ * current backtrace.
+ *
+ * @param context associated with the current suspended state
+ */
+ void suspended(DebugContext context);
+
+ /**
+ * Reports the browser JavaScript virtual machine has resumed. This can happen
+ * asynchronously, due to a user action in the browser (without explicitly
+ * resuming the VM through
+ * {@link DebugContext#continueVm(org.chromium.sdk.DebugContext.StepAction, int, org.chromium.sdk.DebugContext.ContinueCallback)}).
+ */
+ void resumed();
+
+ /**
+ * Reports the debug connection has terminated and {@link JavascriptVm} has stopped operating.
+ * This event is reported always, regardless of which reason causes termination.
+ */
+ void disconnected();
+
+ /**
+ * Reports that a new script has been loaded into a tab.
+ *
+ * @param newScript loaded into the tab
+ */
+ void scriptLoaded(Script newScript);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/ExceptionData.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,32 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+/**
+ * A JavaScript exception data holder for exceptions reported by a JavaScript
+ * virtual machine.
+ */
+public interface ExceptionData {
+
+ /**
+ * @return the thrown exception object
+ */
+ JsObject getExceptionObject();
+
+ /**
+ * @return whether this exception is uncaught
+ */
+ boolean isUncaught();
+
+ /**
+ * @return the text of the source line where the exception was thrown
+ */
+ String getSourceText();
+
+ /**
+ * @return the exception description (plain text)
+ */
+ String getExceptionMessage();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/InvalidContextException.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,26 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+/**
+ * Signals that operation is not available because related {@link DebugContext}
+ * is no more valid. However, there is no guarantee this exception will be thrown
+ * in each case. Note also that {@link DebugContext#continueVm} throws
+ * simple {@link IllegalStateException}.
+ */
+public class InvalidContextException extends RuntimeException {
+ InvalidContextException() {
+ super();
+ }
+ InvalidContextException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ InvalidContextException(String message) {
+ super(message);
+ }
+ public InvalidContextException(Throwable cause) {
+ super(cause);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/JavascriptVm.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,108 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+import java.util.Collection;
+
+import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException;
+
+/**
+ * Abstraction of a remote Javascript virtual machine. Clients can use it to
+ * conduct debugging process. This interface does not specify attach method,
+ * because it cannot be polymorphous.
+ */
+public interface JavascriptVm {
+
+ /**
+ * A callback for breakpoint-related requests.
+ */
+ public interface BreakpointCallback {
+
+ void success(Breakpoint breakpoint);
+
+ void failure(String errorMessage);
+ }
+
+ /**
+ * A callback for retrieving scripts.
+ */
+ public interface ScriptsCallback {
+
+ void success(Collection<Script> scripts);
+
+ void failure(String errorMessage);
+ }
+
+ /**
+ * A callback for suspend request.
+ */
+ public interface SuspendCallback {
+
+ /**
+ * Signals that command successfully finished. After this DebugContext should be built
+ * and unless there are some problems,
+ * {@link DebugEventListener#suspended(DebugContext)} will be called soon.
+ */
+ void success();
+
+ void failure(Exception reason);
+ }
+
+ /**
+ * Detaches from the related tab debugger.
+ *
+ * @return whether the operation succeeded
+ */
+ boolean detach();
+
+ /**
+ * @return whether the tab is currently attached
+ */
+ boolean isAttached();
+
+ /**
+ * Retrieves user scripts loaded into the tab.
+ * Blocks until the result is ready.
+ *
+ * @param callback to invoke once the operation result is available,
+ * may be {@code null}
+ * @throws MethodIsBlockingException if called from a callback because it
+ * blocks until scripts are received
+ */
+ void getScripts(ScriptsCallback callback) throws MethodIsBlockingException;
+
+ /**
+ * Sets a breakpoint with the specified parameters.
+ *
+ * @param type of the breakpoint
+ * @param target of the breakpoint, depends on the {@code type} value:
+ * <table border=1>
+ * <tr><td>type value</td><td>target value</td></tr>
+ * <tr><td>FUNCTION</td><td>a function expression</td></tr>
+ * <tr><td>SCRIPT_NAME</td><td>a script name (as reported by Script#getName())</td></tr>
+ * <tr><td>SCRIPT_ID</td><td>a stringified script ID (as reported by Script#getId())</td></tr>
+ * </table>
+ * @param line in the script or function. If none, use
+ * {@link Breakpoint#EMPTY_VALUE}
+ * @param position of the target start within the line. If none, use
+ * {@link Breakpoint#EMPTY_VALUE}
+ * @param enabled whether the breakpoint is enabled initially
+ * @param condition nullable string with breakpoint condition
+ * @param ignoreCount number specifying the amount of breakpoint hits to
+ * ignore. If none, use {@link Breakpoint#EMPTY_VALUE}
+ * @param callback to invoke when the evaluation result is ready,
+ * may be {@code null}
+ */
+ void setBreakpoint(Breakpoint.Type type, String target, int line, int position, boolean enabled,
+ String condition, int ignoreCount, BreakpointCallback callback);
+
+ /**
+ * Tries to suspend VM. If successful, {@link DebugEventListener#suspended(DebugContext)}
+ * will be called.
+ * @param callback to invoke once the operation result is available,
+ * may be {@code null}
+ */
+ void suspend(SuspendCallback callback);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/JsArray.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,41 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+import java.util.SortedMap;
+
+import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException;
+
+/**
+ * This interface adds methods for handling array elements to the JsObject.
+ */
+public interface JsArray extends JsObject {
+
+ /**
+ * @return the array length (index of the last element plus one),
+ * 0 iff the array is empty
+ */
+ int length();
+
+ /**
+ * @param index in the array
+ * @return a {@code JsVariable} at the {@code index}, or {@code null} if there
+ * is no value at the specified index in the array
+ * @throws MethodIsBlockingException if called from a callback because it may
+ * need to load element data from remote
+ */
+ JsVariable get(int index) throws MethodIsBlockingException;
+
+ /**
+ * @return a map whose keys are array indices and values are {@code
+ * JsVariable} instances found at the corresponding indices. The
+ * resulting map is guaranteed to be sorted in the ascending key
+ * order.
+ * @throws MethodIsBlockingException if called from a callback because
+ * the method needs all elements loaded and might block until
+ * it's done
+ */
+ SortedMap<Integer, ? extends JsVariable> toSparseArray() throws MethodIsBlockingException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/JsFunction.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,23 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+/**
+ * This interface adds methods for handling function properties of JsObject.
+ */
+public interface JsFunction extends JsObject {
+
+ /**
+ * @return script the function resides in or null if script is not available
+ */
+ Script getScript();
+
+ /**
+ * Returns position inside a script. The position is of opening parenthesis of
+ * function arguments, measured in unicode characters from the beginning of script text file.
+ * @return position or -1 if position is not available
+ */
+ int getSourcePosition();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/JsObject.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,61 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+import java.util.Collection;
+
+import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException;
+
+/**
+ * A compound JsValue that has zero or more properties.
+ */
+public interface JsObject extends JsValue {
+
+ /**
+ * @return the class name of this object
+ */
+ String getClassName();
+
+ /**
+ * @return the properties of this compound value
+ * @throws MethodIsBlockingException if called from a callback because it may
+ * need to load value from remote
+ */
+ Collection<? extends JsVariable> getProperties() throws MethodIsBlockingException;
+
+ /**
+ * @return the internal properties of this compound value (e.g. those properties which
+ * are not detectable with the "in" operator: __proto__ etc)
+ * @throws MethodIsBlockingException if called from a callback because it may
+ * need to load value from remote
+ */
+ Collection<? extends JsVariable> getInternalProperties() throws MethodIsBlockingException;
+
+ /**
+ * @param name of the property to get
+ * @return the property object or {@code null} if {@code name} does not
+ * designate an existing object property
+ */
+ JsVariable getProperty(String name);
+
+ /**
+ * @return this object cast to {@link JsArray} or {@code null} if this object
+ * is not an array
+ */
+ JsArray asArray();
+
+ /**
+ * @return this object cast to {@link JsFunction} or {@code null} if this object
+ * is not a function
+ */
+ JsFunction asFunction();
+
+ /**
+ * Optionally returns unique id for this object. No two distinct objects can have the same id.
+ * Lifetime of id may be limited by lifetime of {@link DebugContext}.
+ * @return object id or null
+ */
+ String getRefId();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/JsScope.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,32 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+import java.util.List;
+
+/**
+ * An object that represents a scope in JavaScript.
+ */
+public interface JsScope {
+
+ enum Type {
+ GLOBAL,
+ LOCAL,
+ WITH,
+ CLOSURE,
+ CATCH,
+ UNKNOWN
+ }
+
+ /**
+ * @return type of the scope
+ */
+ Type getType();
+
+ /**
+ * @return the variables known in this scope, in lexicographical order
+ */
+ List<? extends JsVariable> getVariables();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/JsValue.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,105 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+/**
+ * An object that represents a browser JavaScript VM variable value (compound or
+ * atomic.)
+ */
+public interface JsValue {
+
+ /**
+ * Type of a JavaScript value. Two bogus type values (DATE and ARRAY) are
+ * included even though they are not reported by V8. Instead, they are inferred
+ * from the object classname.
+ */
+ public enum Type {
+
+ /**
+ * Object type.
+ */
+ TYPE_OBJECT,
+
+ /**
+ * Number type.
+ */
+ TYPE_NUMBER,
+
+ /**
+ * String type.
+ */
+ TYPE_STRING,
+
+ /**
+ * Function type.
+ */
+ TYPE_FUNCTION,
+
+ /**
+ * Boolean type.
+ */
+ TYPE_BOOLEAN,
+
+ /**
+ * Error type (this one describes a JavaScript exception).
+ */
+ TYPE_ERROR,
+
+ /**
+ * A regular expression.
+ */
+ TYPE_REGEXP,
+
+ /**
+ * An object which is actually a Date. This type is not present in the
+ * protocol but is rather induced from the "object" type and "Date" class of
+ * an object.
+ */
+ TYPE_DATE,
+
+ /**
+ * An object which is actually an array. This type is not present in the
+ * protocol but is rather induced from the "object" type and "Array" class of
+ * an object.
+ */
+ TYPE_ARRAY,
+
+ /**
+ * undefined type.
+ */
+ TYPE_UNDEFINED,
+
+ /**
+ * null type.
+ */
+ TYPE_NULL;
+
+ /**
+ * @param type to check
+ * @return whether {@code type} corresponds to a JsObject
+ */
+ public static boolean isObjectType(Type type) {
+ return type == TYPE_OBJECT || type == TYPE_ARRAY || type == TYPE_ERROR ||
+ type == TYPE_FUNCTION;
+ }
+ }
+
+ /**
+ * @return this value type
+ */
+ Type getType();
+
+ /**
+ * @return a string representation of this value
+ */
+ String getValueString();
+
+ /**
+ * @return this value cast to {@link JsObject} or {@code null} if this value
+ * is not an object
+ */
+ JsObject asObject();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/JsVariable.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,63 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+/**
+ * An object that represents a variable in a browser JavaScript VM call frame.
+ */
+public interface JsVariable {
+
+ /**
+ * A callback to use while setting a variable value.
+ */
+ interface SetValueCallback {
+ void success();
+
+ void failure(String errorMessage);
+ }
+
+ /**
+ * @return whether it is possible to read this variable
+ */
+ boolean isReadable();
+
+ /**
+ * Returns the value of this variable.
+ *
+ * @return a [probably compound] JsValue corresponding to this variable.
+ * {@code null} if there was an error reading the value data
+ * @see #isReadable()
+ * @throws UnsupportedOperationException if this variable is not readable
+ */
+ JsValue getValue() throws UnsupportedOperationException;
+
+ /**
+ * @return the name of this variable
+ */
+ String getName();
+
+ /**
+ * @return whether it is possible to modify this variable
+ */
+ boolean isMutable();
+
+ /**
+ * Sets a new value for this variable.
+ *
+ * @param newValue to set
+ * @param callback to report the operation result to
+ * @see #isMutable()
+ * @throws UnsupportedOperationException if this variable is not mutable
+ */
+ void setValue(String newValue, SetValueCallback callback)
+ throws UnsupportedOperationException;
+
+ /**
+ * @return the fully qualified name of this variable relative to the context
+ * of its call frame
+ */
+ String getFullyQualifiedName();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/Script.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,67 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+
+/**
+ * An objects that holds data for a "script" which is a part of a resource
+ * loaded into the browser, identified by its original document URL, line offset
+ * in the original document, and the line count this script spans.
+ */
+public interface Script {
+
+ /**
+ * Denotes a script type.
+ */
+ enum Type {
+ /** A native, internal JavaScript VM script */
+ NATIVE,
+
+ /** A script supplied by an extension */
+ EXTENSION,
+
+ /** A normal user script */
+ NORMAL
+ }
+
+ /**
+ * @return the script type
+ */
+ Type getType();
+
+ /**
+ * @return the original document URL for this script known by Chromium.
+ * A null name for eval'd scripts
+ */
+ String getName();
+
+ /**
+ * @return the script ID as reported by the JavaScript VM debugger
+ */
+ long getId();
+
+ /**
+ * @return the start line of this script in the original document
+ * (zero-based), inclusive
+ */
+ int getStartLine();
+
+ /**
+ * @return the end line of this script in the original document (zero-based),
+ * inclusive
+ */
+ int getEndLine();
+
+ /**
+ * @return the currently set source text of this script
+ */
+ String getSource();
+
+ /**
+ * @return whether the source for this script is known
+ */
+ boolean hasSource();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/StandaloneVm.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,40 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+import java.io.IOException;
+
+/**
+ * Abstraction of a remote JavaScript virtual machine which is embedded into
+ * some application and accessed via TCP/IP connection to a port opened by
+ * DebuggerAgent. Clients can use it to conduct debugging process.
+ */
+public interface StandaloneVm extends JavascriptVm {
+ /**
+ * Connects to the target VM.
+ *
+ * @param listener to report the debug events to
+ * @throws IOException if there was a transport layer error
+ * @throws UnsupportedVersionException if the SDK protocol version is not
+ * compatible with that supported by the browser
+ */
+ void attach(DebugEventListener listener) throws IOException, UnsupportedVersionException;
+
+ /**
+ * @return name of embedding application as it wished to name itself; might be null
+ */
+ String getEmbedderName();
+
+ /**
+ * @return version of V8 implementation, format is unspecified; must not be null if
+ * {@link StandaloneVm} has been attached
+ */
+ String getVmVersion();
+
+ /**
+ * @return message explaining why VM is detached; may be null
+ */
+ String getDisconnectReason();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/SyncCallback.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,20 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+/**
+ * Secondary callback that should be called after main callback has been
+ * called; it gets called regardless of whether main callback finished
+ * normally or thrown an exception.
+ * It helps to separate callback logic (which may fail) from multi-thread
+ * synchronization (which shouldn't fail). Typically client may release
+ * his semaphore in this callback.
+ */
+public interface SyncCallback {
+ /**
+ * @param e an exception main callback raised or null if none is reported
+ */
+ void callbackDone(RuntimeException e);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/TabDebugEventListener.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,31 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+/**
+ * This interface is used by the SDK to report browser-related debug
+ * events for a certain tab to the clients.
+ */
+public interface TabDebugEventListener {
+ /**
+ * Every {@code TabDebugEventListener} should aggregate
+ * {@code DebugEventListener}.
+ */
+ DebugEventListener getDebugEventListener();
+
+ /**
+ * Reports a navigation event on the target tab.
+ *
+ * @param newUrl the new URL of the debugged tab
+ */
+ void navigated(String newUrl);
+
+ /**
+ * Reports a closing event on the target tab. All following communications
+ * with the associated tab are illegal. This call will be followed by
+ * call to {@link DebugEventListener#disconnected()}.
+ */
+ void closed();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/UnsupportedVersionException.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,41 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+/**
+ * This exception is thrown if the SDK protocol version is not compatible with
+ * that supported by the browser.
+ */
+public class UnsupportedVersionException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+ private final Version localVersion;
+ private final Version remoteVersion;
+
+ public UnsupportedVersionException(Version localVersion, Version remoteVersion) {
+ this(localVersion, remoteVersion, "localVersion=" + localVersion
+ + "; remoteVersion=" + remoteVersion);
+ }
+
+ public UnsupportedVersionException(Version localVersion, Version remoteVersion, String message) {
+ super(message);
+ this.localVersion = localVersion;
+ this.remoteVersion = remoteVersion;
+ }
+
+ /**
+ * @return the protocol version supported by the SDK
+ */
+ public Version getLocalVersion() {
+ return localVersion;
+ }
+
+ /**
+ * @return the incompatible protocol version supported by the browser
+ */
+ public Version getRemoteVersion() {
+ return remoteVersion;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/Version.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,107 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * An object that describes the numeric part of version.
+ */
+public class Version implements Comparable<Version> {
+ private final List<Integer> components;
+
+ /**
+ * Constructs an immutable Version instance given the numeric components of version.
+ */
+ public Version(Integer ... components) {
+ this(Arrays.asList(components));
+ }
+
+ /**
+ * Constructs an immutable Version instance given the numeric components of version.
+ */
+ public Version(List<Integer> components) {
+ this.components = Collections.unmodifiableList(new ArrayList<Integer>(components));
+ }
+
+ /**
+ * @return numeric components of version in form of list of integers
+ */
+ public List<Integer> getComponents() {
+ return components;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof Version)) {
+ return false;
+ }
+ Version that = (Version) obj;
+ return this.components.equals(that.components);
+ }
+
+ @Override
+ public int hashCode() {
+ return components.hashCode();
+ }
+
+ public int compareTo(Version other) {
+ for (int i = 0; i < this.components.size(); i++) {
+ if (other.components.size() <= i) {
+ // shorter version is less
+ return +1;
+ }
+ int res = this.components.get(i).compareTo(other.components.get(i));
+ if (res != 0) {
+ return res;
+ }
+ }
+ if (this.components.size() < other.components.size()) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return components.toString();
+ }
+
+ /**
+ * Parses string as version as far as it is dot-delimited integer numbers.
+ * @param text string representation of version; not null
+ * @return new instance of version or null if text is not version.
+ */
+ public static Version parseString(String text) {
+ int i = 0;
+ List<Integer> components = new ArrayList<Integer>(4);
+ while (i < text.length()) {
+ int num = Character.digit(text.charAt(i), 10);
+ if (num < 0) {
+ break;
+ }
+ i++;
+ while (i < text.length() && Character.digit(text.charAt(i), 10) >= 0) {
+ num = num * 10 + Character.digit(text.charAt(i), 10);
+ i++;
+ }
+ components.add(num);
+ if (i < text.length() && text.charAt(i) == '.') {
+ i++;
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (components.isEmpty()) {
+ return null;
+ }
+ return new Version(components);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/BrowserFactoryImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,58 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.net.SocketAddress;
+
+import org.chromium.sdk.Browser;
+import org.chromium.sdk.BrowserFactory;
+import org.chromium.sdk.ConnectionLogger;
+import org.chromium.sdk.StandaloneVm;
+import org.chromium.sdk.internal.transport.Handshaker;
+import org.chromium.sdk.internal.transport.SocketConnection;
+
+/**
+ * A default implementation of the BrowserFactory interface.
+ */
+public class BrowserFactoryImpl extends BrowserFactory {
+
+ private static final int DEFAULT_CONNECTION_TIMEOUT_MS = 1000;
+
+ @Override
+ public Browser create(SocketAddress socketAddress,
+ ConnectionLogger.Factory connectionLoggerFactory) {
+ SocketConnectionFactory socketConnectionFactory = new SocketConnectionFactory(socketAddress,
+ getTimeout(), connectionLoggerFactory, Handshaker.CHROMIUM);
+ return new BrowserImpl(socketConnectionFactory);
+ }
+
+ // Debug entry (no logger by definition)
+ Browser create(ConnectionFactory connectionFactory) {
+ return new BrowserImpl(connectionFactory);
+ }
+
+ @Override
+ public StandaloneVm createStandalone(SocketAddress socketAddress,
+ ConnectionLogger connectionLogger) {
+ Handshaker.StandaloneV8 handshaker = new Handshaker.StandaloneV8();
+ SocketConnection connection =
+ new SocketConnection(socketAddress, getTimeout(), connectionLogger, handshaker);
+ return new StandaloneVmImpl(connection, handshaker);
+ }
+
+ private int getTimeout() {
+ String timeoutString = System.getProperty(
+ "org.chromium.sdk.client.connection.timeoutMs",
+ String.valueOf(DEFAULT_CONNECTION_TIMEOUT_MS));
+ int timeoutMs = DEFAULT_CONNECTION_TIMEOUT_MS;
+ try {
+ timeoutMs = Integer.parseInt(timeoutString);
+ } catch (NumberFormatException e) {
+ // fall through and use the default value
+ }
+ return timeoutMs;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/BrowserImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,368 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.chromium.sdk.Browser;
+import org.chromium.sdk.BrowserTab;
+import org.chromium.sdk.TabDebugEventListener;
+import org.chromium.sdk.UnsupportedVersionException;
+import org.chromium.sdk.Version;
+import org.chromium.sdk.internal.tools.ToolHandler;
+import org.chromium.sdk.internal.tools.ToolName;
+import org.chromium.sdk.internal.tools.ToolOutput;
+import org.chromium.sdk.internal.tools.devtools.DevToolsServiceHandler;
+import org.chromium.sdk.internal.tools.devtools.DevToolsServiceHandler.TabIdAndUrl;
+import org.chromium.sdk.internal.transport.Connection;
+import org.chromium.sdk.internal.transport.Message;
+import org.chromium.sdk.internal.transport.Connection.NetListener;
+
+/**
+ * A thread-safe implementation of the Browser interface.
+ */
+public class BrowserImpl implements Browser {
+
+ private static final Logger LOGGER = Logger.getLogger(BrowserImpl.class.getName());
+
+ public static final int OPERATION_TIMEOUT_MS = 3000;
+
+ /**
+ * The protocol version supported by this SDK implementation.
+ */
+ public static final Version PROTOCOL_VERSION = new Version(0, 1);
+
+ private final ConnectionSessionManager sessionManager = new ConnectionSessionManager();
+
+ /** The browser connection (gets opened in session). */
+ private final ConnectionFactory connectionFactory;
+
+ BrowserImpl(ConnectionFactory connectionFactory) {
+ this.connectionFactory = connectionFactory;
+ }
+
+ public TabFetcher createTabFetcher() throws IOException, UnsupportedVersionException {
+ SessionManager.Ticket<Session> ticket = connectInternal();
+ return new TabFetcherImpl(ticket);
+ }
+
+ private SessionManager.Ticket<Session> connectInternal() throws IOException,
+ UnsupportedVersionException {
+ try {
+ return sessionManager.connect();
+ } catch (ExceptionWrapper eWrapper) {
+ eWrapper.rethrow();
+ // Not reachable.
+ throw new RuntimeException();
+ }
+ }
+
+ /**
+ * Object that lives during one connection period. Browser should be able to
+ * reconnect (because we want to support attach-detach-attach sequence). On
+ * reconnect new session should be created. Each browser tab should be linked
+ * to a particular session.
+ */
+ public class Session extends SessionManager.SessionBase<Session> {
+
+ private final AtomicBoolean alreadyClosingSession = new AtomicBoolean(false);
+
+ private final CloseableMap<Integer, ToolHandler> tabId2ToolHandler = CloseableMap.newMap();
+
+ // TODO(peter.rybin): get rid of this structure (if we can get rid
+ // of corresponding notification)
+ private final Map<Integer, DebugSession> tabId2DebugSession =
+ new ConcurrentHashMap<Integer, DebugSession>();
+
+ /** The DevTools service handler for the browser. */
+ private volatile DevToolsServiceHandler devToolsHandler;
+
+ /** Open connection which is used by the session. */
+ private final Connection sessionConnection;
+
+ Session() throws IOException, UnsupportedVersionException {
+ super(sessionManager);
+
+ devToolsHandler = new DevToolsServiceHandler(devToolsToolOutput);
+
+ sessionConnection = connectionFactory.newOpenConnection(netListener);
+
+ String serverVersionString;
+ try {
+ serverVersionString = devToolsHandler.version(OPERATION_TIMEOUT_MS);
+ } catch (TimeoutException e) {
+ throw new IOException("Failed to get protocol version from remote", e);
+ }
+ if (serverVersionString == null) {
+ throw new UnsupportedVersionException(BrowserImpl.PROTOCOL_VERSION, null);
+ }
+ Version serverVersion = Version.parseString(serverVersionString);
+ if (serverVersion == null ||
+ serverVersion.compareTo(BrowserImpl.PROTOCOL_VERSION) < 0) {
+ throw new UnsupportedVersionException(BrowserImpl.PROTOCOL_VERSION, serverVersion);
+ }
+ }
+
+ @Override
+ protected Session getThisAsSession() {
+ return this;
+ }
+
+ @Override
+ protected void lastTicketDismissed() {
+ boolean res = alreadyClosingSession.compareAndSet(false, true);
+ if (!res) {
+ // already closing
+ return;
+ }
+ closeSession();
+ sessionConnection.close();
+ }
+
+ void registerTab(int destinationTabId, ToolHandler toolHandler, DebugSession debugSession)
+ throws IOException {
+ try {
+ tabId2ToolHandler.put(destinationTabId, toolHandler);
+ } catch (IllegalStateException e) {
+ throw new IOException("Tab id=" + destinationTabId + " cannot be attached");
+ }
+ tabId2DebugSession.put(destinationTabId, debugSession);
+ }
+
+ void unregisterTab(int destinationTabId) {
+ tabId2DebugSession.remove(destinationTabId);
+ tabId2ToolHandler.remove(destinationTabId);
+ }
+
+ private DevToolsServiceHandler getDevToolsServiceHandler() {
+ return devToolsHandler;
+ }
+
+ @Override
+ protected void checkHealth() {
+ // We do not actually interrupt here. It's more an assert for now: we throw an exception,
+ // if connection is unexpectedly closed.
+ if (sessionConnection.isConnected()) {
+ // All OK
+ return;
+ }
+ // We should not be here
+ LOGGER.severe("checkHealth in BrowserImpl found a consistnecy problem; " +
+ "current session is broken and therefore terminated");
+ interruptSession();
+ closeSession();
+ }
+
+ private void checkConnection() {
+ if (!sessionConnection.isConnected()) {
+ throw new IllegalStateException("connection is not started");
+ }
+ }
+
+ private final NetListener netListener = new NetListener() {
+ public void connectionClosed() {
+ devToolsHandler.onDebuggerDetached();
+ // Use a copy to avoid the underlying map modification in #sessionTerminated
+ // invoked through #onDebuggerDetached
+ Collection<DebugSession> copy = new ArrayList<DebugSession>(tabId2DebugSession.values());
+ for (DebugSession session : copy) {
+ session.onDebuggerDetached();
+ }
+ }
+
+ public void messageReceived(Message message) {
+ ToolName toolName = ToolName.forString(message.getTool());
+ if (toolName == null) {
+ LOGGER.log(Level.SEVERE, "Bad 'Tool' header received: {0}", message.getTool());
+ return;
+ }
+ ToolHandler handler = null;
+ switch (toolName) {
+ case DEVTOOLS_SERVICE:
+ handler = devToolsHandler;
+ break;
+ case V8_DEBUGGER:
+ handler = tabId2ToolHandler.get(Integer.valueOf(message.getDestination()));
+ break;
+ default:
+ LOGGER.log(Level.SEVERE, "Unregistered handler for tool: {0}", message.getTool());
+ return;
+ }
+ if (handler != null) {
+ handler.handleMessage(message);
+ } else {
+ LOGGER.log(
+ Level.SEVERE,
+ "null handler for tool: {0}, destination: {1}",
+ new Object[] {message.getTool(), message.getDestination()});
+ }
+ }
+ public void eosReceived() {
+ boolean res = alreadyClosingSession.compareAndSet(false, true);
+ if (!res) {
+ // already closing
+ return;
+ }
+
+ Collection<ToolHandler> allHandlers = tabId2ToolHandler.close().values();
+ for (ToolHandler handler : allHandlers) {
+ handler.handleEos();
+ }
+
+ devToolsHandler.handleEos();
+ Collection<? extends RuntimeException> problems = interruptSession();
+ for (RuntimeException ex : problems) {
+ LOGGER.log(Level.SEVERE, "Failure in closing connections", ex);
+ }
+ closeSession();
+ }
+ };
+
+ private final ToolOutput devToolsToolOutput = new ToolOutput() {
+ public void send(String content) {
+ Message message =
+ MessageFactory.createMessage(ToolName.DEVTOOLS_SERVICE.value, null, content);
+ sessionConnection.send(message);
+ }
+ };
+
+ public BrowserImpl getBrowser() {
+ return BrowserImpl.this;
+ }
+ }
+
+ private class TabFetcherImpl implements TabFetcher {
+ private final SessionManager.Ticket<Session> ticket;
+
+ TabFetcherImpl(SessionManager.Ticket<Session> ticket) {
+ this.ticket = ticket;
+ }
+
+ public List<? extends TabConnector> getTabs() {
+ Session session = ticket.getSession();
+ session.checkConnection();
+ List<TabIdAndUrl> entries = session.devToolsHandler.listTabs(OPERATION_TIMEOUT_MS);
+ List<TabConnectorImpl> tabConnectors = new ArrayList<TabConnectorImpl>(entries.size());
+ for (TabIdAndUrl entry : entries) {
+ tabConnectors.add(new TabConnectorImpl(entry.id, entry.url, ticket));
+ }
+ return tabConnectors;
+ }
+
+ public void dismiss() {
+ ticket.dismiss();
+ }
+ }
+
+ private class TabConnectorImpl implements TabConnector {
+ private final int tabId;
+ private final String url;
+ // Ticket that we inherit from TabFetcher.
+ private final SessionManager.Ticket<Session> ticket;
+
+ TabConnectorImpl(int tabId, String url, SessionManager.Ticket<Session> ticket) {
+ this.tabId = tabId;
+ this.url = url;
+ this.ticket = ticket;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public boolean isAlreadyAttached() {
+ return ticket.getSession().tabId2ToolHandler.get(tabId) != null;
+ }
+
+ public BrowserTab attach(TabDebugEventListener listener) throws IOException {
+ SessionManager.Ticket<Session> ticket;
+ try {
+ ticket = connectInternal();
+ } catch (UnsupportedVersionException e) {
+ // This exception should have happened on tab fetcher creation.
+ throw new IOException("Unexpected version problem", e);
+ }
+
+ Session session = ticket.getSession();
+
+ BrowserTabImpl browserTab = null;
+ try {
+ browserTab = new BrowserTabImpl(tabId, url, session.sessionConnection, ticket);
+ } finally {
+ if (browserTab == null) {
+ ticket.dismiss();
+ }
+ }
+ // From now on browserTab is responsible for the ticket.
+ browserTab.attach(listener);
+ return browserTab;
+ }
+ }
+
+ /**
+ * With this session manager we expect all ticket owners to call dismiss in any
+ * circumstances.
+ */
+ private class ConnectionSessionManager extends
+ SessionManager<BrowserImpl.Session, ExceptionWrapper> {
+ @Override
+ protected Session newSessionObject() throws ExceptionWrapper {
+ try {
+ return new Session();
+ } catch (IOException e) {
+ throw ExceptionWrapper.wrap(e);
+ } catch (UnsupportedVersionException e) {
+ throw ExceptionWrapper.wrap(e);
+ }
+ }
+ }
+
+ private static abstract class ExceptionWrapper extends Exception {
+ abstract void rethrow() throws IOException, UnsupportedVersionException;
+
+ static ExceptionWrapper wrap(final IOException e) {
+ return new ExceptionWrapper() {
+ @Override
+ void rethrow() throws IOException {
+ throw e;
+ }
+ };
+ }
+
+ static ExceptionWrapper wrap(final UnsupportedVersionException e) {
+ return new ExceptionWrapper() {
+ @Override
+ void rethrow() throws UnsupportedVersionException {
+ throw e;
+ }
+ };
+ }
+ }
+
+ public boolean isTabConnectedForTest(int tabId) {
+ Session session = sessionManager.getCurrentSessionForTest();
+ if (session == null) {
+ return false;
+ }
+ return session.tabId2ToolHandler.get(tabId) != null;
+ }
+
+ public DevToolsServiceHandler getDevToolsServiceHandlerForTests() {
+ return sessionManager.getCurrentSessionForTest().getDevToolsServiceHandler();
+ }
+
+ public boolean isConnectedForTests() {
+ return sessionManager.getCurrentSessionForTest() != null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/BrowserTabImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,151 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.io.IOException;
+
+import org.chromium.sdk.Browser;
+import org.chromium.sdk.BrowserTab;
+import org.chromium.sdk.DebugEventListener;
+import org.chromium.sdk.TabDebugEventListener;
+import org.chromium.sdk.internal.tools.ToolHandler;
+import org.chromium.sdk.internal.tools.ToolName;
+import org.chromium.sdk.internal.tools.ToolOutput;
+import org.chromium.sdk.internal.tools.v8.ChromeDevToolSessionManager;
+import org.chromium.sdk.internal.tools.v8.ChromeDevToolSessionManager.AttachmentFailureException;
+import org.chromium.sdk.internal.transport.Connection;
+import org.chromium.sdk.internal.transport.Message;
+
+/**
+ * A default, thread-safe implementation of the BrowserTab interface.
+ */
+public class BrowserTabImpl extends JavascriptVmImpl implements BrowserTab {
+
+ /** Tab ID as reported by the DevTools server. */
+ private final int tabId;
+
+ /** The primary tab URL. */
+ private final String url;
+
+ private final SessionManager.Ticket<BrowserImpl.Session> connectionTicket;
+
+ private final ChromeDevToolSessionManager devToolSessionManager;
+
+ /** The listener to report debug events to. */
+ private DebugEventListener debugEventListener = null;
+
+ /** The listener to report browser-related debug events to. */
+ private TabDebugEventListener tabDebugEventListener = null;
+
+ public BrowserTabImpl(int tabId, String url, Connection connection,
+ SessionManager.Ticket<BrowserImpl.Session> ticket) throws IOException {
+ this.tabId = tabId;
+ this.url = url;
+ this.connectionTicket = ticket;
+ String tabIdString = String.valueOf(tabId);
+ ChromeDevToolOutput chromeDevToolOutput = new ChromeDevToolOutput(tabIdString, connection);
+ this.devToolSessionManager = new ChromeDevToolSessionManager(this, chromeDevToolOutput);
+
+ ToolHandler toolHandler = devToolSessionManager.getToolHandler();
+ // After this statement we are responsible for dismissing our ticket (we do it via eos message).
+ getBrowserConnectionSession().registerTab(tabId, toolHandler,
+ this.devToolSessionManager.getDebugSession());
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public int getId() {
+ return tabId;
+ }
+
+ @Override
+ public DebugSession getDebugSession() {
+ return devToolSessionManager.getDebugSession();
+ }
+
+ public synchronized TabDebugEventListener getTabDebugEventListener() {
+ return tabDebugEventListener;
+ }
+
+ public Browser getBrowser() {
+ return getBrowserConnectionSession().getBrowser();
+ }
+
+ public BrowserImpl.Session getBrowserConnectionSession() {
+ return connectionTicket.getSession();
+ }
+
+ synchronized void attach(TabDebugEventListener listener) throws IOException {
+ this.tabDebugEventListener = listener;
+ this.debugEventListener = listener.getDebugEventListener();
+
+ boolean normalExit = false;
+ try {
+ Result result;
+ try {
+ result = devToolSessionManager.attachToTab();
+ } catch (AttachmentFailureException e) {
+ throw new IOException(e);
+ }
+ if (Result.OK != result) {
+ throw new IOException("Failed to attach with result: " + result);
+ }
+ normalExit = true;
+ } finally {
+ if (!normalExit) {
+ devToolSessionManager.cutTheLineMyself();
+ }
+ }
+ }
+
+ public boolean detach() {
+ Result result = devToolSessionManager.detachFromTab();
+ return Result.OK == result;
+ }
+
+ public boolean isAttached() {
+ return devToolSessionManager.isAttachedForUi();
+ }
+
+ public void sessionTerminated() {
+ //browserSession.sessionTerminated(this.tabId);
+ }
+
+ public ToolHandler getV8ToolHandler() {
+ return devToolSessionManager.getToolHandler();
+ }
+
+ public ChromeDevToolSessionManager getSessionManager() {
+ return devToolSessionManager;
+ }
+
+ public void handleEosFromToolService() {
+ getBrowserConnectionSession().unregisterTab(tabId);
+ connectionTicket.dismiss();
+ }
+
+ private static class ChromeDevToolOutput implements ToolOutput {
+ private final String destination;
+ private final Connection connection;
+
+ ChromeDevToolOutput(String destination, Connection connection) {
+ this.destination = destination;
+ this.connection = connection;
+ }
+
+
+ public void send(String content) {
+ Message message =
+ MessageFactory.createMessage(ToolName.V8_DEBUGGER.value, destination, content);
+ connection.send(message);
+ }
+ }
+
+ public DebugEventListener getDebugEventListener() {
+ return debugEventListener;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/CallFrameImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,231 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.chromium.sdk.CallFrame;
+import org.chromium.sdk.CallbackSemaphore;
+import org.chromium.sdk.JsScope;
+import org.chromium.sdk.JsVariable;
+import org.chromium.sdk.Script;
+import org.chromium.sdk.SyncCallback;
+import org.chromium.sdk.internal.InternalContext.ContextDismissedCheckedException;
+import org.chromium.sdk.internal.protocol.CommandResponse;
+import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
+import org.chromium.sdk.internal.protocol.data.ValueHandle;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException;
+import org.chromium.sdk.internal.tools.v8.V8CommandProcessor;
+import org.chromium.sdk.internal.tools.v8.V8Helper;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory;
+
+/**
+ * A generic implementation of the CallFrame interface.
+ */
+public class CallFrameImpl implements CallFrame {
+
+ /** The frame ID as reported by the JavaScript VM. */
+ private final int frameId;
+
+ /** The debug context this call frame belongs in. */
+ private final InternalContext context;
+
+ /** The underlying frame data from the JavaScript VM. */
+ private final FrameMirror frameMirror;
+
+ /** The variables known in this call frame. */
+ private Collection<JsVariableImpl> variables = null;
+
+ /** The scopes known in this call frame. */
+ private List<? extends JsScope> scopes = null;
+
+ /** The receiver variable known in this call frame. May be null. */
+ private JsVariable receiverVariable;
+ private boolean receiverVariableLoaded = false;
+
+ /**
+ * Constructs a call frame for the given handler using the FrameMirror data
+ * from the remote JavaScript VM.
+ *
+ * @param mirror frame in the VM
+ * @param index call frame id (0 is the stack top)
+ * @param context in which the call frame is created
+ */
+ public CallFrameImpl(FrameMirror mirror, int index, InternalContext context) {
+ this.context = context;
+ this.frameId = index;
+ this.frameMirror = mirror;
+ }
+
+ public InternalContext getInternalContext() {
+ return context;
+ }
+
+ @Deprecated
+ public Collection<JsVariableImpl> getVariables() {
+ ensureVariables();
+ return variables;
+ }
+
+ public List<? extends JsScope> getVariableScopes() {
+ ensureScopes();
+ return scopes;
+ }
+
+ public JsVariable getReceiverVariable() {
+ ensureReceiver();
+ return this.receiverVariable;
+ }
+
+ private void ensureVariables() {
+ if (variables == null) {
+ this.variables = Collections.unmodifiableCollection(createVariables());
+ }
+ }
+
+ private void ensureScopes() {
+ if (scopes == null) {
+ this.scopes = Collections.unmodifiableList(createScopes());
+ }
+ }
+
+ private void ensureReceiver() {
+ if (!receiverVariableLoaded) {
+ PropertyReference ref = frameMirror.getReceiverRef();
+ if (ref == null) {
+ this.receiverVariable = null;
+ } else {
+ ValueLoader valueLoader = context.getValueLoader();
+ ValueMirror mirror =
+ valueLoader.getOrLoadValueFromRefs(Collections.singletonList(ref)).get(0);
+ this.receiverVariable = new JsVariableImpl(this, mirror, ref.getName());
+ }
+ this.receiverVariableLoaded = true;
+ }
+ }
+
+ public int getLineNumber() {
+ Script script = frameMirror.getScript();
+ // Recalculate respective to the script start
+ // (frameMirror.getLine() returns the line offset in the resource).
+ return script != null
+ ? frameMirror.getLine() - script.getStartLine()
+ : -1;
+ }
+
+ public int getCharStart() {
+ return -1;
+ }
+
+ public int getCharEnd() {
+ return -1;
+ }
+
+ public String getFunctionName() {
+ return frameMirror.getFunctionName();
+ }
+
+ public Script getScript() {
+ return frameMirror.getScript();
+ }
+
+ public void evaluateSync(String expression, EvaluateCallback evaluateCallback)
+ throws MethodIsBlockingException {
+ CallbackSemaphore callbackSemaphore = new CallbackSemaphore();
+ evaluateAsync(expression, evaluateCallback, callbackSemaphore);
+ boolean res = callbackSemaphore.tryAcquireDefault();
+ if (!res) {
+ evaluateCallback.failure("Timeout");
+ }
+ }
+
+ public void evaluateAsync(final String expression, final EvaluateCallback callback,
+ SyncCallback syncCallback) {
+ try {
+ evaluateAsyncImpl(expression, callback, syncCallback);
+ } catch (ContextDismissedCheckedException e) {
+ getInternalContext().getDebugSession().maybeRethrowContextException(e);
+ // or
+ try {
+ callback.failure(e.getMessage());
+ } finally {
+ syncCallback.callbackDone(null);
+ }
+ }
+ }
+ public void evaluateAsyncImpl(final String expression, final EvaluateCallback callback,
+ SyncCallback syncCallback) throws ContextDismissedCheckedException {
+ DebuggerMessage message =
+ DebuggerMessageFactory.evaluate(expression, getIdentifier(), null, null);
+
+ V8CommandProcessor.V8HandlerCallback commandCallback = callback == null
+ ? null
+ : new V8CommandProcessor.V8HandlerCallback() {
+ public void messageReceived(CommandResponse response) {
+ SuccessCommandResponse successResponse = response.asSuccess();
+ if (successResponse != null) {
+ ValueHandle body;
+ try {
+ body = successResponse.getBody().asEvaluateBody();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ JsVariable variable =
+ new JsVariableImpl(CallFrameImpl.this, V8Helper.createMirrorFromLookup(
+ body).getValueMirror(), expression);
+ if (variable != null) {
+ callback.success(variable);
+ } else {
+ callback.failure("Evaluation failed");
+ }
+ } else {
+ callback.failure(response.asFailure().getMessage());
+ }
+ }
+
+ public void failure(String message) {
+ callback.failure(message);
+ }
+ };
+
+ getInternalContext().sendV8CommandAsync(message, true, commandCallback,
+ syncCallback);
+ }
+
+ /**
+ * @return this call frame's unique identifier within the V8 VM (0 is the top
+ * frame)
+ */
+ int getIdentifier() {
+ return frameId;
+ }
+
+ /**
+ * Initializes this frame with variables based on the frameMirror locals.
+ */
+ private Collection<JsVariableImpl> createVariables() {
+ List<PropertyReference> refs = frameMirror.getLocals();
+ List<ValueMirror> mirrors = context.getValueLoader().getOrLoadValueFromRefs(refs);
+ Collection<JsVariableImpl> result = new ArrayList<JsVariableImpl>(refs.size());
+ for (int i = 0; i < refs.size(); i++) {
+ result.add(new JsVariableImpl(this, mirrors.get(i), refs.get(i).getName()));
+ }
+ return result;
+ }
+
+ private List<JsScopeImpl> createScopes() {
+ List<ScopeMirror> scopes = frameMirror.getScopes();
+ List<JsScopeImpl> result = new ArrayList<JsScopeImpl>(scopes.size());
+ for (ScopeMirror mirror : scopes) {
+ result.add(new JsScopeImpl(this, mirror));
+ }
+ return result;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/CloseableMap.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,79 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Utility class that behaves similarly to ConcurrentHashMap, but also
+ * provides {@link #close} operation that stops all new registrations.
+ */
+public class CloseableMap<K, V> {
+ public static <K, V> CloseableMap<K, V> newMap() {
+ return new CloseableMap<K, V>(new ConcurrentHashMap<K, V>());
+ }
+
+ public static <K, V> CloseableMap<K, V> newLinkedMap() {
+ // Creates linked hash map and makes "get" synchronized.
+ return new CloseableMap<K, V>(new LinkedHashMap<K, V>()) {
+ @Override
+ public synchronized V get(K key) {
+ // Base "get" is not synchronized, because we rely on ConcurrentHashMap.
+ return super.get(key);
+ }
+ };
+ }
+
+ private final Map<K, V> map;
+ private boolean mutationClosed = false;
+
+ protected CloseableMap(Map<K, V> map) {
+ this.map = map;
+ }
+
+ public V get(K key) {
+ return map.get(key);
+ }
+
+ public synchronized Map<K, V> close() {
+ if (mutationClosed) {
+ throw new IllegalStateException();
+ }
+ mutationClosed = true;
+ return map;
+ }
+
+ public synchronized V remove(K key) {
+ if (mutationClosed) {
+ // We probably can safely ignore this.
+ return null;
+ }
+ V result = map.remove(key);
+ if (result == null) {
+ throw new IllegalArgumentException("This key is not registered");
+ }
+ return result;
+ }
+
+ public synchronized V removeIfContains(K key) {
+ if (mutationClosed) {
+ // We probably can safely ignore this.
+ return null;
+ }
+ return map.remove(key);
+ }
+
+ public synchronized void put(K key, V value) {
+ if (mutationClosed) {
+ throw new IllegalStateException();
+ }
+ if (map.containsKey(key)) {
+ throw new IllegalStateException("Such key is already registered");
+ }
+ map.put(key, value);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/ConnectionFactory.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,24 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.io.IOException;
+
+import org.chromium.sdk.internal.transport.Connection;
+import org.chromium.sdk.internal.transport.Connection.NetListener;
+
+/**
+ * Factory that can be used when several connections to the same
+ * endpoint are needed. {@link Connection} does not support reconnection, and
+ * this factory can be used instead.
+ */
+public interface ConnectionFactory {
+ /**
+ * Creates new connection and starts it. Does not check whether previous connection
+ * has already finished.
+ * @return already started connection with netListener set
+ */
+ Connection newOpenConnection(NetListener netListener) throws IOException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/ContextBuilder.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,380 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.CallFrame;
+import org.chromium.sdk.DebugContext;
+import org.chromium.sdk.ExceptionData;
+import org.chromium.sdk.Script;
+import org.chromium.sdk.SyncCallback;
+import org.chromium.sdk.internal.protocol.CommandResponse;
+import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
+import org.chromium.sdk.internal.tools.v8.V8CommandProcessor;
+import org.chromium.sdk.internal.tools.v8.V8CommandProcessor.V8HandlerCallback;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory;
+
+public class ContextBuilder {
+ private final DebugSession debugSession;
+
+ /**
+ * Context building process comes though sequence of steps. No one should
+ * be able to work with any step exception the current one.
+ */
+ private Object currentStep = null;
+
+ ContextBuilder(DebugSession debugSession) {
+ this.debugSession = debugSession;
+ }
+
+ public interface ExpectingBreakEventStep {
+ InternalContext getInternalContext();
+ /**
+ * Stores the breakpoints associated with V8 suspension event (empty if an
+ * exception or a step end).
+ *
+ * @param breakpointsHit the breakpoints that were hit
+ */
+ ExpectingBacktraceStep setContextState(Collection<Breakpoint> breakpointsHit,
+ ExceptionData exceptionData);
+ }
+ public interface ExpectingBacktraceStep {
+ InternalContext getInternalContext();
+
+ DebugContext setFrames(FrameMirror[] frameMirrors);
+ }
+
+ /**
+ * Starting point of building new DebugContext process. One should traverse
+ * list of steps to get the result.
+ * @return object representing first step of context building process
+ */
+ public ExpectingBreakEventStep buildNewContext() {
+ assertStep(null);
+
+ final PreContext preContext = new PreContext();
+ final DebugContextData contextData = new DebugContextData();
+
+ return new ExpectingBreakEventStep() {
+ {
+ currentStep = this;
+ }
+
+ public InternalContext getInternalContext() {
+ return preContext;
+ }
+
+ public ExpectingBacktraceStep setContextState(Collection<Breakpoint> breakpointsHit,
+ ExceptionData exceptionData) {
+ assertStep(this);
+
+ DebugContext.State state;
+ if (exceptionData == null) {
+ state = DebugContext.State.NORMAL;
+ } else {
+ state = DebugContext.State.EXCEPTION;
+ }
+
+ contextData.contextState = state;
+ contextData.breakpointsHit = Collections.unmodifiableCollection(breakpointsHit);
+ contextData.exceptionData = exceptionData;
+
+ return new ExpectingBacktraceStep() {
+ {
+ currentStep = this;
+ }
+
+ public InternalContext getInternalContext() {
+ return preContext;
+ }
+
+ public DebugContext setFrames(FrameMirror[] frameMirrors) {
+ assertStep(this);
+
+ contextData.frames = new Frames(frameMirrors, preContext);
+
+ preContext.createContext(contextData);
+
+ DebugContext userContext = preContext.getContext();
+ currentStep = userContext;
+ return userContext;
+ }
+ };
+ }
+ };
+ }
+
+ public ExpectingBreakEventStep buildNewContextWhenIdle() {
+ if (currentStep == null) {
+ return buildNewContext();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Debug session is stopped. Cancel context in any state.
+ */
+ public void forceCancelContext() {
+ // TODO(peter.rybin): complete it
+ }
+
+ public void buildSequenceFailure() {
+ // this means we can't go on debugging
+ // TODO(peter.rybin): implement
+ throw new RuntimeException();
+ }
+
+ private void contextDismissed(DebugContext userContext) {
+ assertStep(userContext);
+ currentStep = null;
+ }
+
+ private void assertStep(Object step) {
+ if (currentStep != step) {
+ throw new IllegalStateException("Expected " + step + ", but was " + currentStep);
+ }
+ }
+
+ private class PreContext implements InternalContext {
+ private final HandleManager handleManager = new HandleManager();
+ private final ValueLoader valueLoader = new ValueLoader(this);
+
+ /**
+ * We synchronize {@link #isValid} state with commands that are being sent
+ * using this monitor.
+ */
+ private final Object sendContextCommandsMonitor = new Object();
+ private volatile boolean isValid = true;
+ private UserContext context = null;
+
+ public boolean isValid() {
+ return isValid;
+ }
+
+ public DebugSession getDebugSession() {
+ return debugSession;
+ }
+
+ public ContextBuilder getContextBuilder() {
+ return ContextBuilder.this;
+ }
+
+ void assertValid() {
+ if (!isValid) {
+ throw new IllegalStateException("This instance of DebugContext cannot be used anymore");
+ }
+ }
+
+ /**
+ * Check if context is valid right now. Throws exception if we are in a strict mode.
+ * Ignores otherwise.
+ */
+ void assertValidForUser() {
+ if (!isValid) {
+ debugSession.maybeRethrowContextException(null);
+ }
+ }
+
+ public UserContext getContext() {
+ if (context == null) {
+ throw new IllegalStateException();
+ }
+ return context;
+ }
+
+ public CallFrameImpl getTopFrameImpl() {
+ assertValid();
+ return getContext().data.frames.getCallFrames().get(0);
+ }
+
+ public HandleManager getHandleManager() {
+ // tolerates dismissed context
+ return handleManager;
+ }
+
+ public ValueLoader getValueLoader() {
+ return valueLoader;
+ }
+
+
+ void createContext(DebugContextData contextData) {
+ if (context != null) {
+ throw new IllegalStateException();
+ }
+ context = new UserContext(contextData);
+ }
+
+ public void sendV8CommandAsync(DebuggerMessage message, boolean isImmediate,
+ V8HandlerCallback commandCallback, SyncCallback syncCallback)
+ throws ContextDismissedCheckedException {
+ synchronized (sendContextCommandsMonitor) {
+ if (!isValid) {
+ throw new ContextDismissedCheckedException();
+ }
+ debugSession.getV8CommandProcessor().sendV8CommandAsync(message, isImmediate,
+ commandCallback, syncCallback);
+ }
+ }
+
+ private void sendMessageAsyncAndIvalidate(DebuggerMessage message,
+ V8CommandProcessor.V8HandlerCallback commandCallback, boolean isImmediate,
+ SyncCallback syncCallback) {
+ synchronized (sendContextCommandsMonitor) {
+ assertValid();
+ debugSession.getV8CommandProcessor().sendV8CommandAsync(message, isImmediate,
+ commandCallback, syncCallback);
+ isValid = false;
+ }
+ }
+
+ private class UserContext implements DebugContext {
+ private final DebugContextData data;
+
+ public UserContext(DebugContextData contextData) {
+ this.data = contextData;
+ }
+
+ public State getState() {
+ assertValidForUser();
+ return data.contextState;
+ }
+ public List<? extends CallFrame> getCallFrames() {
+ assertValidForUser();
+ return data.frames.getCallFrames();
+ }
+
+ public Collection<Breakpoint> getBreakpointsHit() {
+ assertValidForUser();
+ if (data.breakpointsHit == null) {
+ throw new RuntimeException();
+ }
+ return data.breakpointsHit;
+ }
+
+ public ExceptionData getExceptionData() {
+ assertValidForUser();
+ return data.exceptionData;
+ }
+
+ /**
+ * @throws IllegalStateException if context has already been continued
+ */
+ public void continueVm(StepAction stepAction, int stepCount,
+ final ContinueCallback callback) {
+ if (stepAction == null) {
+ throw new NullPointerException();
+ }
+
+ DebuggerMessage message = DebuggerMessageFactory.goOn(stepAction, stepCount);
+ V8CommandProcessor.V8HandlerCallback commandCallback
+ = new V8CommandProcessor.V8HandlerCallback() {
+ public void messageReceived(CommandResponse response) {
+ SuccessCommandResponse successResponse = response.asSuccess();
+ if (successResponse == null) {
+ this.failure(response.asFailure().getMessage());
+ return;
+ }
+
+ contextDismissed(UserContext.this);
+
+ if (callback != null) {
+ callback.success();
+ }
+ getDebugSession().getDebugEventListener().resumed();
+ }
+ public void failure(String message) {
+ synchronized (sendContextCommandsMonitor) {
+ // resurrected
+ isValid = true;
+ }
+ if (callback != null) {
+ callback.failure(message);
+ }
+ }
+ };
+
+ sendMessageAsyncAndIvalidate(message, commandCallback, true, null);
+ }
+
+ InternalContext getInternalContextForTests() {
+ return PreContext.this;
+ }
+ }
+ }
+
+ /**
+ * Simple structure of data which DebugConext implementation uses.
+ */
+ private static class DebugContextData {
+ private Frames frames;
+ /** The breakpoints hit before suspending. */
+ private volatile Collection<Breakpoint> breakpointsHit;
+
+ DebugContext.State contextState;
+ /** The JavaScript exception state. */
+ private ExceptionData exceptionData;
+ }
+
+ private class Frames {
+ /** The frame mirrors while on a breakpoint. */
+ private final FrameMirror[] frameMirrors;
+ /** The cached call frames constructed using frameMirrors. */
+ private final List<CallFrameImpl> unmodifableFrames;
+ private boolean scriptsLinkedToFrames;
+
+ Frames(FrameMirror[] frameMirrors0, InternalContext internalContext) {
+ this.frameMirrors = frameMirrors0;
+ this.scriptsLinkedToFrames = false;
+
+ int frameCount = frameMirrors.length;
+ List<CallFrameImpl> frameList = new ArrayList<CallFrameImpl>(frameCount);
+ for (int i = 0; i < frameCount; ++i) {
+ frameList.add(new CallFrameImpl(frameMirrors[i], i, internalContext));
+ }
+ this.unmodifableFrames = Collections.unmodifiableList(frameList);
+ }
+
+ synchronized List<CallFrameImpl> getCallFrames() {
+ if (!scriptsLinkedToFrames) {
+ // We expect that ALL the V8 scripts are loaded so we can
+ // hook them up to the call frames.
+ int frameCount = frameMirrors.length;
+ for (int i = 0; i < frameCount; ++i) {
+ hookupScriptToFrame(i);
+ }
+ scriptsLinkedToFrames = true;
+ }
+ return unmodifableFrames;
+ }
+
+
+ /**
+ * Associates a script found in the ScriptManager with the given frame.
+ *
+ * @param frameIndex to associate a script with
+ */
+ private void hookupScriptToFrame(int frameIndex) {
+ FrameMirror frame = frameMirrors[frameIndex];
+ if (frame != null && frame.getScript() == null) {
+ Script script = debugSession.getScriptManager().findById(frame.getScriptId());
+ if (script != null) {
+ frame.setScript(script);
+ }
+ }
+ }
+ }
+
+ static InternalContext getInternalContextForTests(DebugContext debugContext) {
+ PreContext.UserContext userContext = (PreContext.UserContext) debugContext;
+ return userContext.getInternalContextForTests();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/DataWithRef.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,36 @@
+package org.chromium.sdk.internal;
+
+import org.chromium.sdk.internal.protocol.data.RefWithDisplayData;
+import org.chromium.sdk.internal.protocol.data.SomeRef;
+
+public abstract class DataWithRef {
+ public abstract long ref();
+
+ /** @return data or null */
+ public abstract RefWithDisplayData getWithDisplayData();
+
+ public static DataWithRef fromSomeRef(final SomeRef someRef) {
+ return new DataWithRef() {
+ @Override
+ public RefWithDisplayData getWithDisplayData() {
+ return someRef.asWithDisplayData();
+ }
+ @Override
+ public long ref() {
+ return someRef.ref();
+ }
+ };
+ }
+ public static DataWithRef fromLong(final long ref) {
+ return new DataWithRef() {
+ @Override
+ public RefWithDisplayData getWithDisplayData() {
+ return null;
+ }
+ @Override
+ public long ref() {
+ return ref;
+ }
+ };
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/DebugSession.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,247 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.Collections;
+
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.DebugEventListener;
+import org.chromium.sdk.InvalidContextException;
+import org.chromium.sdk.SyncCallback;
+import org.chromium.sdk.Version;
+import org.chromium.sdk.JavascriptVm.ScriptsCallback;
+import org.chromium.sdk.JavascriptVm.SuspendCallback;
+import org.chromium.sdk.internal.InternalContext.ContextDismissedCheckedException;
+import org.chromium.sdk.internal.protocol.CommandResponse;
+import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
+import org.chromium.sdk.internal.tools.v8.BreakpointManager;
+import org.chromium.sdk.internal.tools.v8.DefaultResponseHandler;
+import org.chromium.sdk.internal.tools.v8.V8BlockingCallback;
+import org.chromium.sdk.internal.tools.v8.V8CommandOutput;
+import org.chromium.sdk.internal.tools.v8.V8CommandProcessor;
+import org.chromium.sdk.internal.tools.v8.V8Helper;
+import org.chromium.sdk.internal.tools.v8.V8ProtocolUtil;
+import org.chromium.sdk.internal.tools.v8.V8CommandProcessor.V8HandlerCallback;
+import org.chromium.sdk.internal.tools.v8.request.ContextlessDebuggerMessage;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory;
+
+/**
+ * A default, thread-safe implementation of the JsDebugContext interface.
+ */
+public class DebugSession {
+
+ /** The script manager for the associated tab. */
+ private final ScriptManager scriptManager;
+
+ private final V8CommandProcessor v8CommandProcessor;
+
+ /** A helper for performing complex V8-related actions. */
+ private final V8Helper v8Helper = new V8Helper(this);
+
+ private final ContextBuilder contextBuilder;
+
+ /** Our manager. */
+ private DebugSessionManager sessionManager;
+
+ /** Context owns breakpoint manager. */
+ private final BreakpointManager breakpointManager;
+
+ private final ScriptLoader scriptLoader = new ScriptLoader();
+
+ private final DefaultResponseHandler defaultResponseHandler;
+
+ public DebugSession(DebugSessionManager sessionManager, V8ContextFilter contextFilter,
+ V8CommandOutput v8CommandOutput) {
+ this.scriptManager = new ScriptManager(contextFilter);
+ this.sessionManager = sessionManager;
+ this.breakpointManager = new BreakpointManager(this);
+
+ this.defaultResponseHandler = new DefaultResponseHandler(this);
+ this.v8CommandProcessor = new V8CommandProcessor(v8CommandOutput, defaultResponseHandler);
+ this.contextBuilder = new ContextBuilder(this);
+ }
+
+ public ScriptManager getScriptManager() {
+ return scriptManager;
+ }
+
+ public V8CommandProcessor getV8CommandProcessor() {
+ return v8CommandProcessor;
+ }
+
+ public DebugSessionManager getSessionManager() {
+ return sessionManager;
+ }
+
+ public void onDebuggerDetached() {
+ getSessionManager().onDebuggerDetached();
+ getScriptManager().reset();
+ contextBuilder.forceCancelContext();
+ }
+
+ /**
+ * Sends V8 command messages, but only those which doesn't depend on context.
+ * Use {@code InternalContext} if you need to send context-specific commands.
+ */
+ public void sendMessageAsync(ContextlessDebuggerMessage message, boolean isImmediate,
+ V8CommandProcessor.V8HandlerCallback commandCallback, SyncCallback syncCallback) {
+ v8CommandProcessor.sendV8CommandAsync(message, isImmediate,
+ commandCallback, syncCallback);
+ }
+
+ /**
+ * Gets invoked when a navigation event is reported by the browser tab.
+ */
+ public void navigated() {
+ getScriptManager().reset();
+ }
+
+ /**
+ * @return the DebugEventListener associated with this context
+ */
+ public DebugEventListener getDebugEventListener() {
+ return getSessionManager().getDebugEventListener();
+ }
+
+ public BreakpointManager getBreakpointManager() {
+ return breakpointManager;
+ }
+
+ public ScriptLoader getScriptLoader() {
+ return scriptLoader;
+ }
+
+ public V8Helper getV8Helper() {
+ return v8Helper;
+ }
+
+ public ContextBuilder getContextBuilder() {
+ return contextBuilder;
+ }
+
+ public void suspend(final SuspendCallback suspendCallback) {
+ V8CommandProcessor.V8HandlerCallback v8Callback = new V8CommandProcessor.V8HandlerCallback() {
+ public void failure(String message) {
+ if (suspendCallback != null) {
+ suspendCallback.failure(new Exception(message));
+ }
+ }
+ public void messageReceived(CommandResponse response) {
+ SuccessCommandResponse successResponse = response.asSuccess();
+ if (successResponse == null) {
+ if (suspendCallback != null) {
+ suspendCallback.failure(new Exception("Unsuccessful command"));
+ }
+ return;
+ }
+ if (suspendCallback != null) {
+ suspendCallback.success();
+ }
+
+ ContextBuilder.ExpectingBreakEventStep step1 = contextBuilder.buildNewContextWhenIdle();
+ if (step1 == null) {
+ return;
+ }
+ ContextBuilder.ExpectingBacktraceStep step2 =
+ step1.setContextState(Collections.<Breakpoint>emptyList(), null);
+ defaultResponseHandler.getBreakpointProcessor().processNextStep(step2);
+ }
+ };
+ sendMessageAsync(DebuggerMessageFactory.suspend(), true, v8Callback, null);
+ }
+
+ public class ScriptLoader {
+
+ /** Whether the initial script loading has completed. */
+ private volatile boolean doneInitialScriptLoad = false;
+
+ /**
+ * Loads all scripts from the remote if necessary, and feeds them into the
+ * callback provided (if any).
+ *
+ * @param callback nullable callback to invoke when the scripts are ready
+ */
+ public void loadAllScripts(final ScriptsCallback callback, SyncCallback syncCallback) {
+ if (!doneInitialScriptLoad) {
+ this.doneInitialScriptLoad = true;
+ // Not loaded the scripts initially, do full load.
+ v8Helper.reloadAllScriptsAsync(new V8HandlerCallback() {
+ public void messageReceived(CommandResponse response) {
+ if (callback != null) {
+ SuccessCommandResponse successResponse = response.asSuccess();
+ if (successResponse != null) {
+ callback.success(getScriptManager().allScripts());
+ } else {
+ callback.failure(response.asFailure().getMessage());
+ }
+ }
+ }
+
+ public void failure(String message) {
+ if (callback != null) {
+ callback.failure(message);
+ }
+ }
+ }, syncCallback);
+ } else {
+ try {
+ if (callback != null) {
+ callback.success(getScriptManager().allScripts());
+ }
+ } finally {
+ if (syncCallback != null) {
+ syncCallback.callbackDone(null);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks version of V8 and check if it in running state.
+ */
+ public void startCommunication() {
+ V8BlockingCallback<Void> callback = new V8BlockingCallback<Void>() {
+ @Override
+ public Void messageReceived(CommandResponse response) {
+ SuccessCommandResponse successResponse = response.asSuccess();
+ if (successResponse == null) {
+ return null;
+ }
+ Version vmVersion = V8ProtocolUtil.parseVersionResponse(successResponse);
+
+ if (V8VersionMilestones.isRunningAccurate(vmVersion)) {
+ Boolean running = successResponse.running();
+ if (running == Boolean.FALSE) {
+ ContextBuilder.ExpectingBreakEventStep step1 = contextBuilder.buildNewContextWhenIdle();
+ // If step is not null -- we are already in process of building a context.
+ if (step1 != null) {
+ ContextBuilder.ExpectingBacktraceStep step2 =
+ step1.setContextState(Collections.<Breakpoint>emptyList(), null);
+
+ defaultResponseHandler.getBreakpointProcessor().processNextStep(step2);
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected Void handleSuccessfulResponse(SuccessCommandResponse response) {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ V8Helper.callV8Sync(this.v8CommandProcessor, DebuggerMessageFactory.version(), callback);
+ }
+
+ public void maybeRethrowContextException(ContextDismissedCheckedException e) {
+ // TODO(peter.rybin): make some kind of option out of this
+ final boolean strictPolicy = true;
+ if (strictPolicy) {
+ throw new InvalidContextException(e);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/DebugSessionManager.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,25 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import org.chromium.sdk.DebugEventListener;
+
+/**
+ * Type that manages debug session as it's represented to V8 core debugging
+ * classes. Basically it's an internal interface of JavascriptVm object.
+ */
+public interface DebugSessionManager {
+
+ /**
+ * Listener is kept by session manager.
+ */
+ DebugEventListener getDebugEventListener();
+
+ /**
+ * Debugger detached event goes through {@code DebugContextImpl},
+ * and {@code DebugContextImpl} should notify upwards via this method.
+ */
+ void onDebuggerDetached();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/ExceptionDataImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,52 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import org.chromium.sdk.ExceptionData;
+import org.chromium.sdk.JsObject;
+
+/**
+ * An immutable implementation of the ExceptionData interface.
+ */
+public class ExceptionDataImpl implements ExceptionData {
+
+ private final InternalContext context;
+ private final String sourceText;
+ private final ValueMirror mirror;
+ private final String name;
+ private final boolean isUncaught;
+ private final String exceptionText;
+ private JsObject cachedException;
+
+ public ExceptionDataImpl(InternalContext context, ValueMirror mirror, String name,
+ boolean isUncaught, String sourceText, String exceptionText) {
+ this.context = context;
+ this.mirror = mirror;
+ this.name = name;
+ this.isUncaught = isUncaught;
+ this.sourceText = sourceText;
+ this.exceptionText = exceptionText;
+ }
+
+ public JsObject getExceptionObject() {
+ if (cachedException == null) {
+ cachedException = new JsObjectImpl(context.getTopFrameImpl(), name, mirror);
+ }
+ return cachedException;
+ }
+
+ public String getSourceText() {
+ return sourceText;
+ }
+
+ public boolean isUncaught() {
+ return isUncaught;
+ }
+
+ public String getExceptionMessage() {
+ return exceptionText;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/FrameMirror.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,95 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.List;
+
+import org.chromium.sdk.Script;
+import org.chromium.sdk.internal.protocol.FrameObject;
+import org.chromium.sdk.internal.tools.v8.V8Helper;
+
+/**
+ * A representation of a remote JavaScript VM call frame.
+ */
+public class FrameMirror {
+
+ /**
+ * A name of the script associated with the frame.
+ */
+ private final String scriptName;
+
+ /**
+ * 0-based line number in the entire script resource.
+ */
+ private final int lineNumber;
+
+ /**
+ * Function name associated with the frame.
+ */
+ private final String frameFunction;
+
+ /**
+ * The associated script id value.
+ */
+ private final long scriptId;
+
+ /**
+ * A script associated with the frame.
+ */
+ private Script script;
+
+ /**
+ * The JSON descriptor of the frame.
+ */
+ private final FrameObject frameObject;
+
+ public FrameMirror(FrameObject frameObject,
+ String scriptName, int line, long scriptId, String frameFunction) {
+ this.frameObject = frameObject;
+ this.scriptName = scriptName;
+ this.lineNumber = line;
+ this.scriptId = scriptId;
+ this.frameFunction = frameFunction;
+ }
+
+ public String getScriptName() {
+ return scriptName;
+ }
+
+ public long getScriptId() {
+ return scriptId;
+ }
+
+ /**
+ * @return the 0-based line number in the resource
+ */
+ public int getLine() {
+ return lineNumber;
+ }
+
+ public String getFunctionName() {
+ return frameFunction;
+ }
+
+ public List<PropertyReference> getLocals() {
+ return V8Helper.computeLocals(frameObject);
+ }
+
+ public List<ScopeMirror> getScopes() {
+ return V8Helper.computeScopes(frameObject);
+ }
+
+ public PropertyReference getReceiverRef() {
+ return V8Helper.computeReceiverRef(frameObject);
+ }
+
+ public synchronized void setScript(Script script) {
+ this.script = script;
+ }
+
+ public synchronized Script getScript() {
+ return script;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/FunctionAdditionalProperties.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,36 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+/**
+ * A holder for the function-specific properties.
+ */
+public class FunctionAdditionalProperties {
+ private final int sourcePosition;
+ private final int scriptId;
+
+ public FunctionAdditionalProperties(int sourcePosition, int scriptId) {
+ this.sourcePosition = sourcePosition;
+ this.scriptId = scriptId;
+ }
+
+ /**
+ * @return source position or {@link #NO_POSITION} if position is not available
+ */
+ public int getSourcePosition() {
+ return sourcePosition;
+ }
+
+ public static final int NO_POSITION = -1;
+
+ /**
+ * @return script id or {@link #NO_SCRIPT_ID} if script is not available
+ */
+ public int getScriptId() {
+ return scriptId;
+ }
+
+ public static final int NO_SCRIPT_ID = -1;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/HandleManager.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,60 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.chromium.sdk.internal.protocol.data.SomeHandle;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.v8.V8ProtocolUtil;
+import org.json.simple.JSONObject;
+
+/**
+ * A facility for storage and retrieval of handle objects using their "ref" IDs.
+ */
+public class HandleManager {
+
+ private final ConcurrentMap<Long, SomeHandle> refToHandle =
+ new ConcurrentHashMap<Long, SomeHandle>();
+
+ public void putAll(List<SomeHandle> list) {
+ for (SomeHandle handle : list) {
+ put(handle.handle(), handle);
+ }
+ }
+
+ SomeHandle put(Long ref, JSONObject object) {
+ SomeHandle smthWithHandle;
+ try {
+ smthWithHandle = V8ProtocolUtil.getV8Parser().parse(object, SomeHandle.class);
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ put(ref, smthWithHandle);
+ return smthWithHandle;
+ }
+
+ private void put(Long ref, SomeHandle smthWithHandle) {
+ SomeHandle oldObject = refToHandle.putIfAbsent(ref, smthWithHandle);
+ if (oldObject != null) {
+ mergeValues(oldObject, smthWithHandle);
+ }
+ }
+
+ void put(SomeHandle someHandle) {
+ if (someHandle.handle() >= 0) {
+ put(someHandle.handle(), someHandle);
+ }
+ }
+
+ public SomeHandle getHandle(Long ref) {
+ return refToHandle.get(ref);
+ }
+
+ private static void mergeValues(SomeHandle oldObject, SomeHandle newObject) {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/InternalContext.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,52 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import org.chromium.sdk.SyncCallback;
+import org.chromium.sdk.internal.tools.v8.V8CommandProcessor;
+import org.chromium.sdk.internal.tools.v8.V8CommandSender;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage;
+
+/**
+ * Internal API to DebugContext implementation. The actual object might
+ * be hidden deep inside, so we need an interface. Do not try to cast
+ * DebugContext to this interface -- technically they might be different
+ * objects.
+ */
+public interface InternalContext extends V8CommandSender<DebuggerMessage,
+ InternalContext.ContextDismissedCheckedException> {
+ /**
+ * Context belongs to a particular {@code DebugSession}.
+ * @return DebugSession this context belongs to
+ */
+ DebugSession getDebugSession();
+
+ ContextBuilder getContextBuilder();
+
+ // TODO(peter.rybin): document this
+ boolean isValid();
+
+ /**
+ * Handle manager makes sense only for a particular context.
+ * @return HandleManager of this context
+ */
+ HandleManager getHandleManager();
+
+ CallFrameImpl getTopFrameImpl();
+
+ /**
+ * Sends V8 command message provided this context is still valid. There is no
+ * way of making sure context will be valid via this API.
+ * @throws ContextDismissedCheckedException if context is not valid anymore
+ */
+ void sendV8CommandAsync(DebuggerMessage message, boolean isImmediate,
+ V8CommandProcessor.V8HandlerCallback commandCallback, SyncCallback syncCallback)
+ throws ContextDismissedCheckedException;
+
+ class ContextDismissedCheckedException extends Exception {
+ }
+
+ ValueLoader getValueLoader();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/JavascriptVmImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,42 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.CallbackSemaphore;
+import org.chromium.sdk.JavascriptVm;
+import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException;
+
+/**
+ * Base implementation of JavascriptVm.
+ */
+public abstract class JavascriptVmImpl implements JavascriptVm {
+
+ protected JavascriptVmImpl() {
+ }
+
+ public void suspend(SuspendCallback callback) {
+ getDebugSession().suspend(callback);
+ }
+
+ public void getScripts(ScriptsCallback callback) throws MethodIsBlockingException {
+ CallbackSemaphore callbackSemaphore = new CallbackSemaphore();
+ getDebugSession().getScriptLoader().loadAllScripts(callback, callbackSemaphore);
+
+ boolean res = callbackSemaphore.tryAcquireDefault();
+ if (!res) {
+ callback.failure("Timeout");
+ }
+ }
+
+ public void setBreakpoint(Breakpoint.Type type, String target, int line,
+ int position, boolean enabled, String condition, int ignoreCount,
+ BreakpointCallback callback) {
+ getDebugSession().getBreakpointManager()
+ .setBreakpoint(type, target, line, position, enabled, condition, ignoreCount, callback);
+ }
+
+ protected abstract DebugSession getDebugSession();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/JsArrayImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,155 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.chromium.sdk.JsArray;
+import org.chromium.sdk.JsVariable;
+import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException;
+
+/**
+ * A generic implementation of the JsArray interface.
+ */
+class JsArrayImpl extends JsObjectImpl implements JsArray {
+
+ /**
+ * An indexed sparse array of elements. Keys are indices, values are elements.
+ */
+ private SortedMap<Integer, JsVariableImpl> indexToElementMap;
+
+ /**
+ * This constructor implies lazy resolution of object properties.
+ *
+ * @param callFrame this array belongs in
+ * @param parentFqn the fully qualified name of this array parent
+ * @param valueState the mirror corresponding to this array
+ */
+ JsArrayImpl(CallFrameImpl callFrame, String parentFqn, ValueMirror valueState) {
+ super(callFrame, parentFqn, valueState);
+ }
+
+ private synchronized void ensureElementsMap() throws MethodIsBlockingException {
+ if (indexToElementMap != null) {
+ return;
+ }
+ SortedMap<Integer, JsVariableImpl> map =
+ // TODO(peter.rybin): do we need this comparator at all?
+ new TreeMap<Integer, JsVariableImpl>(new Comparator<Integer>() {
+ public int compare(Integer o1, Integer o2) {
+ return o1 - o2;
+ }
+ });
+
+ for (JsVariableImpl prop : getProperties()) {
+ String name = prop.getRawName();
+ Integer index = getAsArrayIndex(name);
+ if (index == null) {
+ continue;
+ }
+ map.put(index, prop);
+ }
+ indexToElementMap = Collections.unmodifiableSortedMap(map);
+ }
+
+ public JsVariable get(int index) throws MethodIsBlockingException {
+ ensureElementsMap();
+ return indexToElementMap.get(index);
+ }
+
+ public SortedMap<Integer, ? extends JsVariable> toSparseArray() throws MethodIsBlockingException {
+ ensureElementsMap();
+ return indexToElementMap;
+ }
+
+ public int length() {
+ // TODO(peter.rybin) optimize it: either read "length" from remote or count PropertyReference
+ // rather than JsVariableImpl
+ int lastIndex = -1;
+ List<JsVariableImpl> properties = getSubpropertiesHelper().getPropertiesLazily();
+ // TODO(peter.rybin): rename propRefs
+ for (JsVariableImpl prop : properties) {
+ String name = prop.getRawName();
+ Integer index = getAsArrayIndex(name);
+ if (index == null) {
+ continue;
+ }
+ if (index > lastIndex) {
+ lastIndex = index;
+ }
+ }
+ return lastIndex + 1;
+ }
+
+ @Override
+ public String toString() {
+ SortedMap<Integer, ? extends JsVariable> elements;
+ try {
+ elements = toSparseArray();
+ } catch (MethodIsBlockingException e) {
+ return "[JsArray: Exception in retrieving data]";
+ }
+ StringBuilder result = new StringBuilder();
+ result.append("[JsArray: length=").append(elements.size());
+ for (Map.Entry<Integer, ? extends JsVariable> entry : elements.entrySet()) {
+ result.append(',').append(entry.getKey()).append('=').append(entry.getValue());
+ }
+ result.append(']');
+ return result.toString();
+ }
+
+ @Override
+ public JsArrayImpl asArray() {
+ return this;
+ }
+
+ @Override
+ protected JsVariableImpl.NameDecorator getChildPropertyNameDecorator() {
+ return ARRAY_ELEMENT_DECORATOR;
+ }
+
+ /**
+ * @return integer representation of the index or null if it is not an integer
+ */
+ static Integer getAsArrayIndex(String varName) {
+ if (!JsonUtil.isInteger(varName)) {
+ return null;
+ }
+ try {
+ int index = Integer.parseInt(varName);
+ return index;
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ }
+
+ private final static JsVariableImpl.NameDecorator ARRAY_ELEMENT_DECORATOR =
+ new JsVariableImpl.NameDecorator() {
+ @Override
+ String decorateVarName(String rawName) {
+ Integer index = getAsArrayIndex(rawName);
+ if (index == null) {
+ return rawName;
+ }
+ // Fix array element indices
+ return OPEN_BRACKET + rawName + CLOSE_BRACKET;
+ }
+ @Override
+ String buildAccessSuffix(String rawName) {
+ Integer index = getAsArrayIndex(rawName);
+ if (index == null) {
+ return NOOP.buildAccessSuffix(rawName);
+ }
+ return OPEN_BRACKET + rawName + CLOSE_BRACKET;
+ }
+ private static final String OPEN_BRACKET = "[";
+ private static final String CLOSE_BRACKET = "]";
+ };
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/JsDataTypeUtil.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,89 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.chromium.sdk.JsValue.Type;
+
+/**
+ * A utility that facilitates retrieval of {@link Type}s according to the
+ * JSON values received.
+ */
+public class JsDataTypeUtil {
+
+ private static Map<String, Type> jsonTypeToEnum = new HashMap<String, Type>();
+
+ private static Map<Type, String> enumToJsonType = new EnumMap<Type, String>(Type.class);
+
+ /**
+ * Class name of an Array object (TYPE_ARRAY).
+ */
+ public static final String CLASSNAME_ARRAY = "Array";
+
+ /**
+ * Class name of a Date object (TYPE_DATE).
+ */
+ private static final String CLASSNAME_DATE = "Date";
+
+ static {
+ put("object", Type.TYPE_OBJECT);
+ put("number", Type.TYPE_NUMBER);
+ put("string", Type.TYPE_STRING);
+ put("function", Type.TYPE_FUNCTION);
+ put("boolean", Type.TYPE_BOOLEAN);
+ put("undefined", Type.TYPE_UNDEFINED);
+ put("null", Type.TYPE_NULL);
+ put("error", Type.TYPE_ERROR);
+ put("array", Type.TYPE_ARRAY);
+ put("date", Type.TYPE_DATE);
+ put("regexp", Type.TYPE_REGEXP);
+ }
+
+ /**
+ * Gets a JsDataType using a V8 JavaScript type and, optionally, a class name
+ * of the object. If {@code className} is {@code null}, only the 1:1 mapping
+ * shall be used.
+ *
+ * @param jsonType the JS type from a JSON response
+ * @param className a nullable class name of the object
+ * @return a JsDataType corresponding to {@code jsonType} and, possibly,
+ * modified according to {@code className}
+ */
+ public static Type fromJsonTypeAndClassName(String jsonType, String className) {
+ if (jsonType == null) {
+ return null;
+ }
+ if (CLASSNAME_DATE.equals(className)) {
+ // hack to use the TYPE_DATE type even though its type in V8 is "object"
+ return Type.TYPE_DATE;
+ } else if (CLASSNAME_ARRAY.equals(className)) {
+ // hack to use the TYPE_ARRAY type even though its type in V8 is "object"
+ return Type.TYPE_ARRAY;
+ }
+ return jsonTypeToEnum.get(jsonType);
+ }
+
+ /**
+ * Converts {@code type} to its JSON representation used in V8.
+ *
+ * @param type to convert
+ * @return a string name of the type understandable by V8
+ */
+ public static String getJsonString(Type type) {
+ return enumToJsonType.get(type);
+ }
+
+ private static void put(String jsonString, Type type) {
+ jsonTypeToEnum.put(jsonString, type);
+ enumToJsonType.put(type, jsonString);
+ }
+
+ private JsDataTypeUtil() {
+ // not instantiable
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/JsFunctionImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,41 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import org.chromium.sdk.JsFunction;
+import org.chromium.sdk.Script;
+
+/**
+ * Generic implementation of {@link JsFunction}.
+ */
+class JsFunctionImpl extends JsObjectImpl implements JsFunction {
+ JsFunctionImpl(CallFrameImpl callFrame, String parentFqn, ValueMirror valueState) {
+ super(callFrame, parentFqn, valueState);
+ }
+
+ public Script getScript() {
+ FunctionAdditionalProperties additionalProperties =
+ (FunctionAdditionalProperties) getSubpropertiesMirror().getAdditionalProperties();
+
+ int scriptId = additionalProperties.getScriptId();
+ if (scriptId == FunctionAdditionalProperties.NO_SCRIPT_ID) {
+ return null;
+ }
+ DebugSession debugSession = getCallFrame().getInternalContext().getDebugSession();
+ return debugSession.getScriptManager().findById(Long.valueOf(scriptId));
+ }
+
+ public int getSourcePosition() {
+ FunctionAdditionalProperties additionalProperties =
+ (FunctionAdditionalProperties) getSubpropertiesMirror().getAdditionalProperties();
+
+ return additionalProperties.getSourcePosition();
+ }
+
+ @Override
+ public JsFunction asFunction() {
+ return this;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/JsObjectImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,206 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.chromium.sdk.JsFunction;
+import org.chromium.sdk.JsObject;
+import org.chromium.sdk.JsVariable;
+import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException;
+
+/**
+ * A generic implementation of the JsObject interface.
+ */
+class JsObjectImpl extends JsValueImpl implements JsObject {
+
+ private final CallFrameImpl callFrame;
+
+ private final String parentFqn;
+
+ /**
+ * This constructor implies the lazy resolution of object properties.
+ *
+ * @param callFrame where this instance belongs in
+ * @param parentFqn the fully qualified name of the object parent
+ * @param valueState the value data from the JS VM
+ */
+ JsObjectImpl(CallFrameImpl callFrame, String parentFqn, ValueMirror valueState) {
+ super(valueState);
+ this.callFrame = callFrame;
+ this.parentFqn = parentFqn;
+ }
+
+ public Collection<JsVariableImpl> getProperties() throws MethodIsBlockingException {
+ return subproperties.getPropertiesLazily();
+ }
+
+ public Collection<JsVariableImpl> getInternalProperties() throws MethodIsBlockingException {
+ return internalProperties.getPropertiesLazily();
+ }
+
+ public String getRefId() {
+ int ref = getMirror().getRef();
+ if (ref < 0) {
+ // Negative handle means that it's transient. We don't expose it.
+ return null;
+ } else {
+ return String.valueOf(ref);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ result.append("[JsObject: type=").append(getType());
+ try {
+ for (JsVariable prop : getProperties()) {
+ result.append(',').append(prop);
+ }
+ } catch (MethodIsBlockingException e) {
+ return "[JsObject: Exception in retrieving data]";
+ }
+ result.append(']');
+ return result.toString();
+ }
+
+ @Override
+ public JsObjectImpl asObject() {
+ return this;
+ }
+
+ public JsArrayImpl asArray() {
+ return null;
+ }
+
+ public JsFunction asFunction() {
+ return null;
+ }
+
+ public JsVariable getProperty(String name) {
+ return subproperties.getProperty(name);
+ }
+
+ public String getClassName() {
+ return getMirror().getClassName();
+ }
+
+ protected JsVariableImpl.NameDecorator getChildPropertyNameDecorator() {
+ return JsVariableImpl.NameDecorator.NOOP;
+ }
+
+ protected CallFrameImpl getCallFrame() {
+ return callFrame;
+ }
+
+ Subproperties getSubpropertiesHelper() {
+ return subproperties;
+ }
+
+ protected SubpropertiesMirror getSubpropertiesMirror() {
+ ValueLoader valueLoader = callFrame.getInternalContext().getValueLoader();
+
+ return valueLoader.loadSubpropertiesInMirror(getMirror()).getSubpropertiesMirror();
+ }
+
+ abstract class Subproperties {
+ private List<JsVariableImpl> properties = null;
+ private Map<String, JsVariableImpl> propertyMap = null;
+
+ /**
+ * Calls to this method must be synchronized on propertyLock.
+ */
+ private Map<String, JsVariableImpl> ensurePropertyMap() {
+ if (propertyMap == null) {
+ List<JsVariableImpl> propertiesList = getPropertiesLazily();
+ Map<String, JsVariableImpl> map =
+ new HashMap<String, JsVariableImpl>(propertiesList.size() * 2, 0.75f);
+ for (JsVariableImpl prop : propertiesList) {
+ map.put(prop.getName(), prop);
+ }
+ propertyMap = Collections.unmodifiableMap(map);
+ }
+ return propertyMap;
+ }
+
+
+ private List<JsVariableImpl> createPropertiesFromMirror(List<ValueMirror> mirrorProperties,
+ List<? extends PropertyReference> propertyRefs) throws MethodIsBlockingException {
+ // TODO(peter.rybin) Maybe assert that context is valid here
+
+ List<JsVariableImpl> result = new ArrayList<JsVariableImpl>(mirrorProperties.size());
+ for (int i = 0; i < mirrorProperties.size(); i++) {
+ ValueMirror mirror = mirrorProperties.get(i);
+ String varName = propertyRefs.get(i).getName();
+ String fqn = getFullyQualifiedName(varName);
+ if (fqn == null) {
+ continue;
+ }
+ result.add(new JsVariableImpl(callFrame, mirror, varName, fqn,
+ getNameDecorator()));
+ }
+ return result;
+ }
+
+ private String getFullyQualifiedName(String propName) {
+ if (propName.startsWith(".")) {
+ // ".arguments" is not legal
+ return null;
+ }
+ return parentFqn + getNameDecorator().buildAccessSuffix(propName);
+ }
+
+ JsVariableImpl getProperty(String propertyName) {
+ return ensurePropertyMap().get(propertyName);
+ }
+
+ List<JsVariableImpl> getPropertiesLazily() throws MethodIsBlockingException {
+ synchronized (this) {
+ if (properties == null) {
+
+ List<? extends PropertyReference> propertyRefs = getPropertyRefs(getSubpropertiesMirror());
+ ValueLoader valueLoader = callFrame.getInternalContext().getValueLoader();
+ List<ValueMirror> subMirrors = valueLoader.getOrLoadValueFromRefs(propertyRefs);
+
+ List<JsVariableImpl> wrappedProperties = createPropertiesFromMirror(subMirrors,
+ propertyRefs);
+ properties = Collections.unmodifiableList(wrappedProperties);
+ }
+ return properties;
+ }
+ }
+
+ abstract JsVariableImpl.NameDecorator getNameDecorator();
+ abstract List<? extends PropertyReference> getPropertyRefs(
+ SubpropertiesMirror subpropertiesMirror);
+ }
+
+ private final Subproperties subproperties = new Subproperties() {
+ @Override
+ JsVariableImpl.NameDecorator getNameDecorator() {
+ return getChildPropertyNameDecorator();
+ }
+ @Override
+ List<? extends PropertyReference> getPropertyRefs(SubpropertiesMirror subpropertiesMirror) {
+ return subpropertiesMirror.getProperties();
+ }
+ };
+
+ private final Subproperties internalProperties = new Subproperties() {
+ @Override
+ JsVariableImpl.NameDecorator getNameDecorator() {
+ return JsVariableImpl.NameDecorator.NOOP;
+ }
+ @Override
+ List<? extends PropertyReference> getPropertyRefs(SubpropertiesMirror subpropertiesMirror) {
+ return subpropertiesMirror.getInternalProperties();
+ }
+ };
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/JsScopeImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,62 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.chromium.sdk.JsScope;
+import org.chromium.sdk.JsVariable;
+
+/**
+ * A generic implementation of the JsScope interface.
+ */
+public class JsScopeImpl implements JsScope {
+
+ private final CallFrameImpl callFrameImpl;
+ private final ScopeMirror mirror;
+ private List<JsVariable> properties = null;
+
+ public JsScopeImpl(CallFrameImpl callFrameImpl, ScopeMirror mirror) {
+ this.callFrameImpl = callFrameImpl;
+ this.mirror = mirror;
+ }
+
+ public Type getType() {
+ Type type = CODE_TO_TYPE.get(mirror.getType());
+ if (type == null) {
+ type = Type.UNKNOWN;
+ }
+ return type;
+ }
+
+ public synchronized List<? extends JsVariable> getVariables() {
+ if (properties == null) {
+ ValueLoader valueLoader = callFrameImpl.getInternalContext().getValueLoader();
+ List<? extends PropertyReference> propertyRefs =
+ valueLoader.loadScopeFields(mirror.getIndex(), callFrameImpl.getIdentifier());
+ List<ValueMirror> propertyMirrors = valueLoader.getOrLoadValueFromRefs(propertyRefs);
+
+ properties = new ArrayList<JsVariable>(propertyMirrors.size());
+ for (int i = 0; i < propertyMirrors.size(); i++) {
+ properties.add(new JsVariableImpl(callFrameImpl, propertyMirrors.get(i),
+ propertyRefs.get(i).getName()));
+ }
+ }
+ return properties;
+ }
+
+ private static final Map<Integer, Type> CODE_TO_TYPE;
+ static {
+ CODE_TO_TYPE = new HashMap<Integer, Type>();
+ CODE_TO_TYPE.put(0, Type.GLOBAL);
+ CODE_TO_TYPE.put(1, Type.LOCAL);
+ CODE_TO_TYPE.put(2, Type.WITH);
+ CODE_TO_TYPE.put(3, Type.CLOSURE);
+ CODE_TO_TYPE.put(4, Type.CATCH);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/JsValueImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,42 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import org.chromium.sdk.JsValue;
+
+/**
+ * A base class that represents a JavaScript VM variable value (compound values
+ * are represented by subclasses.)
+ */
+class JsValueImpl implements JsValue {
+
+ /** The value data as reported by the JavaScript VM. */
+ private final ValueMirror valueData;
+
+ JsValueImpl(ValueMirror valueData) {
+ this.valueData = valueData;
+ }
+
+ public Type getType() {
+ return valueData.getType();
+ }
+
+ public String getValueString() {
+ return valueData.toString();
+ }
+
+ public JsObjectImpl asObject() {
+ return null;
+ }
+
+ public ValueMirror getMirror() {
+ return this.valueData;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[JsValue: type=%s,value=%s]", getType(), getValueString());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/JsVariableImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,154 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+
+import org.chromium.sdk.JsVariable;
+import org.chromium.sdk.JsValue.Type;
+
+/**
+ * A generic implementation of the JsVariable interface.
+ */
+public class JsVariableImpl implements JsVariable {
+
+ /**
+ * The variable value data as reported by the JavaScript VM (is used to
+ * construct the variable value.)
+ */
+ private final ValueMirror valueData;
+
+ /** The call frame this variable belongs in. */
+ private final CallFrameImpl callFrame;
+
+ /** The fully qualified name of this variable. */
+ private final String variableFqn;
+
+ private final NameDecorator nameDecorator;
+
+ /** The lazily constructed value of this variable. */
+ private final JsValueImpl value;
+
+ /** Variable name. */
+ private final String rawName;
+
+ /**
+ * Constructs a variable contained in the given call frame with the given
+ * value mirror.
+ *
+ * @param callFrame that owns this variable
+ * @param valueData value data for this variable
+ */
+ JsVariableImpl(CallFrameImpl callFrame, ValueMirror valueData, String name) {
+ this(callFrame, valueData, name, null, NameDecorator.NOOP);
+ }
+
+ /**
+ * Constructs a variable contained in the given call frame with the given
+ * value mirror.
+ *
+ * @param callFrame that owns this variable
+ * @param valueData for this variable
+ * @param variableFqn the fully qualified name of this variable
+ */
+ JsVariableImpl(CallFrameImpl callFrame, ValueMirror valueData, String name, String variableFqn,
+ NameDecorator nameDecorator) {
+ this.callFrame = callFrame;
+ this.valueData = valueData;
+ this.rawName = name;
+ this.variableFqn = variableFqn;
+ this.nameDecorator = nameDecorator;
+
+ Type type = this.valueData.getType();
+ switch (type) {
+ case TYPE_FUNCTION:
+ this.value = new JsFunctionImpl(callFrame, this.variableFqn, this.valueData);
+ break;
+ case TYPE_ERROR:
+ case TYPE_OBJECT:
+ this.value = new JsObjectImpl(callFrame, this.variableFqn, this.valueData);
+ break;
+ case TYPE_ARRAY:
+ this.value = new JsArrayImpl(callFrame, this.variableFqn, this.valueData);
+ break;
+ default:
+ this.value = new JsValueImpl(this.valueData);
+ }
+ }
+
+ /**
+ * @return a [probably compound] JsValue corresponding to this variable.
+ * {@code null} if there was an error lazy-loading the value data.
+ */
+ public JsValueImpl getValue() {
+ return value;
+ }
+
+ public String getName() {
+ return nameDecorator.decorateVarName(rawName);
+ }
+
+ public String getRawName() {
+ return this.rawName;
+ }
+
+ public boolean isMutable() {
+ return false; // TODO(apavlov): fix once V8 supports it
+ }
+
+ public boolean isReadable() {
+ // TODO(apavlov): implement once the readability metadata are available
+ return true;
+ }
+
+ public synchronized void setValue(String newValue, SetValueCallback callback) {
+ // TODO(apavlov): currently V8 does not support it
+ if (!isMutable()) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("[JsVariable: name=")
+ .append(getName())
+ .append(",value=")
+ .append(getValue())
+ .append(']')
+ .toString();
+ }
+
+ /**
+ * Returns the call frame owning this variable.
+ */
+ protected CallFrameImpl getCallFrame() {
+ return callFrame;
+ }
+
+ public ValueMirror getMirror() {
+ return valueData;
+ }
+
+ public String getFullyQualifiedName() {
+ return variableFqn != null
+ ? variableFqn
+ : getName();
+ }
+
+ static abstract class NameDecorator {
+ static final NameDecorator NOOP = new NameDecorator() {
+ @Override
+ String decorateVarName(String rawName) {
+ return rawName;
+ }
+ @Override
+ String buildAccessSuffix(String rawName) {
+ return "." + rawName;
+ }
+ };
+ abstract String decorateVarName(String rawName);
+ abstract String buildAccessSuffix(String rawName);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/JsonException.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+/**
+ * Signals incorrect (or unexpected) JSON content.
+ */
+public class JsonException extends RuntimeException {
+
+ JsonException() {
+ }
+
+ JsonException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ JsonException(String message) {
+ super(message);
+ }
+
+ JsonException(Throwable cause) {
+ super(cause);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/JsonUtil.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,216 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONStreamAware;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+/**
+ * A utility for JSON-related data conversion.
+ */
+public class JsonUtil {
+
+ private static final Logger LOGGER = Logger.getLogger(JsonUtil.class.getName());
+
+ /**
+ * Converts a JSONStreamAware into a String.
+ *
+ * @param object the object to convert
+ * @return a JSON String representation of the object
+ */
+ public static String streamAwareToJson(JSONStreamAware object) {
+ StringWriter out = new StringWriter();
+ try {
+ object.writeJSONString(out);
+ } catch (IOException e) {
+ return null;
+ }
+ return out.toString();
+ }
+
+ /**
+ * @param json a JSON representation of an object (rather than an array or any
+ * other type)
+ * @return a JSONObject represented by json, or null if json does not
+ * represent a valid JSONObject
+ * @throws ParseException
+ */
+ public static JSONObject jsonObjectFromJson(String json) throws ParseException {
+ JSONParser p = new JSONParser();
+ Object parsed = p.parse(json);
+ if (false == parsed instanceof JSONObject) {
+ LOGGER.log(Level.SEVERE, "Not a JSON object: {0}", json);
+ return null;
+ }
+ return (JSONObject) parsed;
+ }
+
+ /**
+ * Helper function to rip out an integer number from a JSON payload.
+ *
+ * @param obj JSON payload
+ * @param key to look up
+ * @return null if key not found or bad type
+ */
+ public static Long getAsLong(JSONObject obj, CharSequence key) {
+ String keyString = key.toString();
+ Object v = obj.get(keyString);
+ if (v instanceof Long || v == null) {
+ return (Long) v;
+ }
+
+ LOGGER.log(Level.SEVERE, "Key: {0}, found value: {1}", new Object[] {keyString, v});
+ return null;
+ }
+
+ /**
+ * Helper function to rip out a double from a JSON payload.
+ *
+ * @param obj JSON payload
+ * @param key to look up
+ * @return null if key not found or bad type
+ */
+ public static Double getAsDouble(JSONObject obj, CharSequence key) {
+ String keyString = key.toString();
+ Object v = obj.get(keyString);
+ if (v instanceof Double || v == null) {
+ return (Double) v;
+ }
+ LOGGER.log(Level.SEVERE, "Key: {0}, found value: {1}", new Object[] {keyString, v});
+ return null;
+ }
+
+ /**
+ * Helper function to rip out a string from a JSON payload.
+ *
+ * @param obj JSON payload
+ * @param key to look up
+ * @return null if key not found or bad type
+ */
+ public static String getAsString(JSONObject obj, CharSequence key) {
+ String keyString = key.toString();
+ Object v = obj.get(keyString);
+ if (v instanceof String || v == null) {
+ return (String) v;
+ }
+ return String.valueOf(v);
+ }
+
+ /**
+ * Helper function to rip out a Boolean from a JSON payload.
+ *
+ * @param obj JSON payload
+ * @param key to look up
+ * @return Boolean.FALSE if key not found
+ */
+ public static Boolean getAsBoolean(JSONObject obj, CharSequence key) {
+ String keyString = key.toString();
+ Object v = obj.get(keyString);
+ if (v instanceof Boolean || v == null) {
+ return v != null
+ ? (Boolean) v
+ : false;
+ }
+
+ LOGGER.log(Level.SEVERE, "Key: {0}, found value: {1}", new Object[] {keyString, v});
+ return false;
+ }
+
+ /**
+ * Helper function to rip out a nested JSON object from the payload.
+ *
+ * @param obj JSON payload
+ * @param key to look up
+ * @return null if key not found
+ */
+ public static JSONObject getAsJSON(JSONObject obj, CharSequence key) {
+ String keyString = key.toString();
+ Object v = obj.get(keyString);
+ if (v instanceof JSONObject || v == null) {
+ return (JSONObject) v;
+ }
+
+ LOGGER.log(Level.SEVERE, "Key: {0}, found value: {1}", new Object[] {keyString, v});
+ return null;
+ }
+
+ /**
+ * Helper function to rip out a nested JSON object from the payload or throw an exception.
+ * @param obj JSON payload
+ * @param key to look up
+ * @return not null
+ * @throws JsonException if failed to rip out the object
+ */
+ public static JSONObject getAsJSONStrict(JSONObject obj, CharSequence key) {
+ JSONObject result = getAsJSON(obj, key);
+ if (result == null) {
+ throw new JsonException("Failed to find property '" + key);
+ }
+ return result;
+ }
+
+ /**
+ * Helper function to rip out a JSONArray from the payload.
+ *
+ * @param obj JSON payload
+ * @param key to look up
+ * @return null if key not found
+ */
+ public static JSONArray getAsJSONArray(JSONObject obj, CharSequence key) {
+ String keyString = key.toString();
+ Object v = obj.get(keyString);
+ if (v instanceof JSONArray || v == null) {
+ return (JSONArray) v;
+ }
+
+ LOGGER.log(Level.SEVERE, "Key: {0}, found value: {1}", new Object[] {keyString, v});
+ return null;
+ }
+
+ /**
+ * Helper function to rip out a JSONArray from the payload or throw an exception.
+ *
+ * @param obj JSON payload
+ * @param key to look up
+ * @return not null
+ * @throws JsonException if failed to rip out the array
+ */
+ public static JSONArray getAsJSONArrayStrict(JSONObject obj, CharSequence key) {
+ JSONArray result = getAsJSONArray(obj, key);
+ if (result == null) {
+ throw new JsonException("Failed to find property '" + key + "' of array type");
+ }
+ return result;
+ }
+
+ /**
+ * @param value to check
+ * @return whether the value can be parsed as an integer
+ */
+ public static boolean isInteger(String value) {
+ try {
+ Integer.parseInt(value);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ public static String quoteString(String string) {
+ return "\"" + string + "\"";
+ }
+
+ private JsonUtil() {
+ // not instantiable
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/MessageFactory.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,32 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.chromium.sdk.internal.transport.Message;
+import org.chromium.sdk.internal.transport.Message.Header;
+
+/**
+ * A facility that creates transport {@link Message}s for sending requests to
+ * Chromium using the available ChromeDevTools Protocol commands.
+ */
+public class MessageFactory {
+ public static Message createMessage(String tool, String destination, String content) {
+ Map<String, String> headers = new HashMap<String, String>();
+ if (tool != null) {
+ headers.put(Header.TOOL.name, tool);
+ }
+ if (destination != null) {
+ headers.put(Header.DESTINATION.name, destination);
+ }
+ return new Message(headers, content);
+ }
+
+ private MessageFactory() {
+ // not instantiable
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/PropertyHoldingValueMirror.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,33 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+/**
+ * A representation of a properties data of a value in the remote JavaScript VM.
+ * Must be immutable. Conceptually it always corresponds to a {@link ValueMirror}
+ * and in a way should behave like dynamic subclass of ValueMirror.
+ */
+public class PropertyHoldingValueMirror {
+ private final ValueMirror valueMirror;
+ private final SubpropertiesMirror subpropertiesMirror;
+
+ PropertyHoldingValueMirror(ValueMirror valueMirror) {
+ this.valueMirror = valueMirror;
+ this.subpropertiesMirror = SubpropertiesMirror.EMPTY;
+ }
+
+ PropertyHoldingValueMirror(ValueMirror valueMirror, SubpropertiesMirror subpropertiesMirror) {
+ this.valueMirror = valueMirror;
+ this.subpropertiesMirror = subpropertiesMirror;
+ }
+
+ public ValueMirror getValueMirror() {
+ return valueMirror;
+ }
+
+ public SubpropertiesMirror getSubpropertiesMirror() {
+ return subpropertiesMirror;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/PropertyReference.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,36 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+
+/**
+ * A named property reference.
+ */
+public class PropertyReference {
+ private final String name;
+
+ private final DataWithRef smthWithRef;
+
+ /**
+ * @param propertyName the name of the property
+ * @param valueObject a JSON descriptor of the property
+ */
+ public PropertyReference(String propertyName, DataWithRef smthWithRef) {
+ this.name = propertyName;
+ this.smthWithRef = smthWithRef;
+ }
+
+ public long getRef() {
+ return smthWithRef.ref();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DataWithRef getValueObject() {
+ return smthWithRef;
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/PropertyType.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,45 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Known V8 VM property types. The default is NORMAL.
+ */
+public enum PropertyType {
+ NORMAL(0),
+ FIELD(1),
+ CONSTANT_FUNCTION(2),
+ CALLBACKS(3),
+ INTERCEPTOR(4),
+ MAP_TRANSITION(5),
+ CONSTANT_TRANSITION(6),
+ NULL_DESCRIPTOR(7),
+ ;
+
+ public final int value;
+
+ private PropertyType(int value) {
+ this.value = value;
+ }
+
+ private static Map<Integer, PropertyType> valueToTypeMap = new HashMap<Integer, PropertyType>();
+
+ static {
+ for (PropertyType type : values()) {
+ valueToTypeMap.put(type.value, type);
+ }
+ }
+
+ public static PropertyType forValue(Integer value) {
+ if (value == null) {
+ return null;
+ }
+ return valueToTypeMap.get(value);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/Result.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,53 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A V8 debugger attachment/detachment operation result.
+ */
+public enum Result {
+
+ /** The operation went fine. */
+ OK(0),
+
+ /** The tab attachment status is illegal for the specified operation. */
+ ILLEGAL_TAB_STATE(1),
+
+ /** The tab specified is not known. */
+ UNKNOWN_TAB(2),
+
+ /** A generic debugger error occurred. */
+ DEBUGGER_ERROR(3),
+
+ /** An unknown command was specified. */
+ UNKNOWN_COMMAND(4), ;
+
+ public final int code;
+
+ private static final Map<Integer, Result> codeToResult = new HashMap<Integer, Result>();
+
+ static {
+ for (Result result : values()) {
+ codeToResult.put(result.code, result);
+ }
+ }
+
+ private Result(int code) {
+ this.code = code;
+ }
+
+ /**
+ * Gets a Result value for the given code.
+ *
+ * @param code to look up the Result for
+ * @return a Result value for {@code code} or {@code null} if code is unknown
+ */
+ public static Result forCode(int code) {
+ return codeToResult.get(code);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/ScopeMirror.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,32 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+/**
+ * Datum that describes scope piece of information that comes from "scopes" response.
+ */
+public class ScopeMirror {
+
+ public ScopeMirror(int type, int index) {
+ this.type = type;
+ this.index = index;
+ }
+
+ int getType() {
+ return type;
+ }
+
+ int getIndex() {
+ return index;
+ }
+
+ @Override
+ public String toString() {
+ return "scope type=" + type + " index=" + index;
+ }
+
+ private final int type;
+ private final int index;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/ScriptImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,182 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.List;
+
+import org.chromium.sdk.Script;
+import org.chromium.sdk.internal.protocol.data.ScriptHandle;
+import org.chromium.sdk.internal.protocol.data.SomeHandle;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.v8.V8ProtocolUtil;
+
+/**
+ * An objects that holds data for a "script" which is a part of a resource
+ * loaded into the browser, identified by its original document URL, line offset
+ * in the original document, and the line count this script spans.
+ */
+public class ScriptImpl implements Script {
+
+ /**
+ * An object containing data that uniquely identify a V8 script chunk.
+ */
+ public static class Descriptor {
+ public final Type type;
+
+ public final String name;
+
+ public final int lineOffset;
+
+ public final int endLine;
+
+ public final long id;
+
+ public Descriptor(Type type, long id, String name, int lineOffset, int lineCount) {
+ this.type = type;
+ this.id = id;
+ this.name = name;
+ this.lineOffset = lineOffset;
+ this.endLine = lineOffset + lineCount - 1;
+ }
+
+ @Override
+ public int hashCode() {
+ return
+ name != null ? name.hashCode() : (int) id * 0x101 +
+ lineOffset * 0x1001 +
+ endLine * 0x10001;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof Descriptor)) {
+ return false;
+ }
+ Descriptor that = (Descriptor) obj;
+ // The id equality is stronger than the name equality.
+ return this.id == that.id &&
+ this.lineOffset == that.lineOffset &&
+ this.endLine == that.endLine;
+ }
+
+ public static Descriptor forResponse(ScriptHandle script, List<SomeHandle> refs,
+ V8ContextFilter contextFilter) {
+ script = V8ProtocolUtil.validScript(script, refs, contextFilter);
+ if (script == null) {
+ return null;
+ }
+ String name = script.name();
+ try {
+ Long scriptType = script.scriptType();
+ Type type = V8ProtocolUtil.getScriptType(scriptType);
+ if (type == null) {
+ return null;
+ }
+ int lineOffset = (int) script.lineOffset();
+ int lineCount = (int) script.lineCount();
+ int id = V8ProtocolUtil.getScriptIdFromResponse(script).intValue();
+ return new Descriptor(type, id, name, lineOffset, lineCount);
+ } catch (Exception e) {
+ // not a script object has been passed in
+ return null;
+ }
+ }
+ }
+
+ private final Descriptor descriptor;
+
+ private String source;
+
+ /**
+ * @param descriptor of the script retrieved from a "scripts" response
+ */
+ public ScriptImpl(Descriptor descriptor) {
+ this.descriptor = descriptor;
+ this.source = null;
+ }
+
+ public Type getType() {
+ return this.descriptor.type;
+ }
+
+ public String getName() {
+ return descriptor.name;
+ }
+
+ public int getStartLine() {
+ return descriptor.lineOffset;
+ }
+
+ public int getEndLine() {
+ return descriptor.endLine;
+ }
+
+ public long getId() {
+ return descriptor.id;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ public boolean hasSource() {
+ return source != null;
+ }
+
+ public void setSource(String source) {
+ this.source = source;
+ }
+
+ @Override
+ public int hashCode() {
+ return
+ descriptor.hashCode() * 0x101 +
+ (hasSource() ? (source.hashCode() * 0x1001) : 0);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof ScriptImpl)) {
+ return false;
+ }
+ ScriptImpl that = (ScriptImpl) obj;
+ return this.descriptor.equals(that.descriptor) && eq(this.source, that.source);
+ }
+
+ private static boolean eq(Object left, Object right) {
+ return left == right || (left != null && left.equals(right));
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[Script (").append(hasSource()
+ ? "has"
+ : "no").append(" source): name=").append(getName()).append(", lineRange=[").append(
+ getStartLine()).append(';').append(getEndLine()).append("]]");
+ return sb.toString();
+ }
+
+ public static Long getScriptId(HandleManager handleManager, long scriptRef) {
+ SomeHandle handle = handleManager.getHandle(scriptRef);
+ if (handle == null) {
+ return -1L; // not found
+ }
+ ScriptHandle scriptHandle;
+ try {
+ scriptHandle = handle.asScriptHandle();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ return scriptHandle.id();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/ScriptManager.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,153 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.chromium.sdk.Script;
+import org.chromium.sdk.internal.ScriptImpl.Descriptor;
+import org.chromium.sdk.internal.protocol.data.ScriptHandle;
+import org.chromium.sdk.internal.protocol.data.SomeHandle;
+import org.chromium.sdk.internal.tools.v8.V8ProtocolUtil;
+
+/**
+ * Manages scripts known in the corresponding browser tab.
+ */
+public class ScriptManager {
+
+ public interface Callback {
+ /**
+ * This method gets invoked for every script in the manager.
+ *
+ * @param script to process
+ * @return whether other scripts should be processed. If false, the #forEach
+ * method terminates.
+ */
+ boolean process(Script script);
+ }
+
+ /**
+ * Maps script id's to scripts.
+ */
+ private final Map<Long, ScriptImpl> idToScript =
+ Collections.synchronizedMap(new HashMap<Long, ScriptImpl>());
+
+ private final V8ContextFilter contextFilter;
+
+ ScriptManager(V8ContextFilter contextFilter) {
+ this.contextFilter = contextFilter;
+ }
+
+ /**
+ * Adds a script using a "script" V8 response.
+ *
+ * @param scriptBody to add the script from
+ * @param refs that contain the associated script debug context
+ * @return the new script, or {@code null} if the response does not contain
+ * a valid script JSON
+ */
+ public synchronized Script addScript(ScriptHandle scriptBody, List<SomeHandle> refs) {
+
+ ScriptImpl theScript = findById(V8ProtocolUtil.getScriptIdFromResponse(scriptBody));
+
+ if (theScript == null) {
+ Descriptor desc = Descriptor.forResponse(scriptBody, refs, contextFilter);
+ if (desc == null) {
+ return null;
+ }
+ theScript = new ScriptImpl(desc);
+ idToScript.put(desc.id, theScript);
+ }
+ if (scriptBody.source() != null) {
+ setSourceCode(scriptBody, theScript);
+ }
+
+ return theScript;
+ }
+
+ /**
+ * Associates a source received in a "source" V8 response with the given
+ * script.
+ *
+ * @param scriptBody the JSON response body
+ * @param script the script to associate the source with
+ */
+ public void setSourceCode(ScriptHandle body, ScriptImpl script) {
+ String src = body.source();
+ if (src == null) {
+ return;
+ }
+ if (script != null) {
+ script.setSource(src);
+ }
+ }
+
+ /**
+ * @param id of the script to find
+ * @return the script with {@code id == ref} or {@code null} if none found
+ */
+ public ScriptImpl findById(Long id) {
+ return idToScript.get(id);
+ }
+
+ /**
+ * Determines whether all scripts added into this manager have associated
+ * sources.
+ *
+ * @return whether all known scripts have associated sources
+ */
+ public boolean isAllSourcesLoaded() {
+ final boolean[] result = new boolean[1];
+ result[0] = true;
+ forEach(new Callback() {
+ public boolean process(Script script) {
+ if (!script.hasSource()) {
+ result[0] = false;
+ return false;
+ }
+ return true;
+ }
+ });
+ return result[0];
+ }
+
+ public Collection<Script> allScripts() {
+ final Collection<Script> result = new HashSet<Script>();
+ forEach(new Callback() {
+ public boolean process(Script script) {
+ result.add(script);
+ return true;
+ }
+ });
+ return result;
+ }
+
+ /**
+ * This method allows running the same code for all scripts in the manager.
+ *
+ * @param callback to invoke for every script, until
+ * {@link Callback#process(Script)} returns {@code false}.
+ */
+ public synchronized void forEach(Callback callback) {
+ for (Script script : idToScript.values()) {
+ if (!callback.process(script)) {
+ return;
+ }
+ }
+ }
+
+ public void reset() {
+ idToScript.clear();
+ }
+
+ public V8ContextFilter getContextFilter() {
+ return contextFilter;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/SessionManager.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,242 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Manager that switches on and off some resource for a shared multiuser access.
+ * The nature of actual resource should be defined in subclass of
+ * {@link SessionManager}. Time period when resource is on is called "session".
+ * Switch on operation (aka session creation) must be an atomic operation.
+ * Switch off (aka session closing) may be lengthy asynchronous operation.
+ * <p>
+ * If no user needs it, manager switches the resource off. On the first demand
+ * resource gets switched on (and new session gets created). After the last user
+ * has released the resource, the session finishes either instantly or
+ * some time later. In the latter case resource becomes temporary unavailable.
+ * The manager does not operate resource in any other sense than switching it
+ * on and off.
+ * <p>
+ * Every user first acquires the resource by calling {@link #connect()} method.
+ * It gets ticket which points to the corresponding session. Method
+ * {@link Ticket#dismiss()} must be called when resource is no more needed.
+ * @param <SESSION> user class that represents a session; must
+ * extend {@link SessionBase}
+ * @param <EX> exception that is allowed to be thrown when resource is being switched
+ * on and the new session is starting; {@link RuntimeException}
+ * is a good default parameter value
+ */
+public abstract class SessionManager<SESSION extends SessionManager.SessionBase<SESSION>,
+ EX extends Exception> {
+
+ // Holds current session; all access must be synchronized on "this".
+ private SESSION currentSession = null;
+
+ /**
+ * Ticket to resource use. Every client gets its own copy. All tickets must
+ * be dismissed in order for resource to be switched off.
+ * @param <SESSION> is be the same type as of manager that issued this ticket
+ */
+ public interface Ticket<SESSION> {
+ /**
+ * Each valid ticket points to session of the resource. The actual type
+ * {@code SESSION} is provided by user (as a type parameter of enclosing
+ * SessionManager). The actual resource should be accessible from
+ * {@code SESSION}.
+ * @return non-null current session
+ * @throws IllegalStateException if ticket is no longer valid
+ */
+ SESSION getSession();
+
+ /**
+ * Releases resource and makes ticket invalid. Switches the resource
+ * off if it was a last ticket.
+ * @throws IllegalStateException if ticket is no more valid
+ */
+ void dismiss();
+ }
+
+ /**
+ * Registers user request for resource and switches the resource on if required.
+ * @return new ticket which symbolize use of resource until
+ * {@link Ticket#dismiss()} is called
+ * @throws EX if connect required creating a new session and if the new session creation
+ * has failed
+ */
+ public Ticket<SESSION> connect() throws EX {
+ synchronized (this) {
+ if (currentSession != null) {
+ // this may reset currentSession
+ currentSession.checkHealth();
+ }
+ if (currentSession == null) {
+ currentSession = newSessionObject();
+ if (currentSession.manager != this) {
+ throw new IllegalArgumentException("Wrong manager was set in session");
+ }
+ }
+ return currentSession.newTicket();
+ }
+ }
+
+ /**
+ * User-provided constructor of a new session. It should switch the resource on
+ * whatever it actually means.
+ * @return new instance of resource use session
+ * @throws EX if switching resource on or creating a new session failed
+ */
+ protected abstract SESSION newSessionObject() throws EX;
+
+ /**
+ * Base class for user session. It should be subclassed and it is parameterized by
+ * this subclass. Object construction should have semantics of switching resource
+ * on. It gets constructed via user-defined {@link SessionManager#newSessionObject()}.
+ * Subclass should honestly pass instance of {@link SessionManager} to the base
+ * class. User also should implement {@link #lastTicketDismissed()} and helper
+ * {@link #getThisAsSession()}.
+ * @param <SESSION> the very user class which extends {@link SessionBase};
+ * {@link #getThisAsSession()} should compile as "return this;"
+ */
+ public static abstract class SessionBase<SESSION extends SessionBase<SESSION>> {
+ private final SessionManager<?, ?> manager;
+ private boolean isConnectionStopped = false;
+ private boolean isCancelled = false;
+
+ SessionBase(SessionManager<SESSION, ?> manager) {
+ this.manager = manager;
+ }
+
+ /**
+ * Must be simply "return this;"
+ */
+ protected abstract SESSION getThisAsSession();
+
+ /**
+ * Session may check its health here. This check is made on
+ * every new connection. If it appears that the session is no longer alive
+ * the method should call {@link #interruptSession()}. However, this is a highly
+ * unwanted scenario: session should interrupt itself synchronously, no
+ * on-demand from this method.
+ */
+ protected abstract void checkHealth();
+
+ /**
+ * User-provided behavior when no more valid tickets left. Resource should
+ * be switched off whatever it actually means and the session closed.
+ * There are 3 options here:
+ * <ol>
+ * <li>Method is finished with {@link #closeSession()} call. Method
+ * {@link SessionManager#connect()} does not interrupt its service and simply
+ * creates new session the next call.
+ * <li>Method is finished with {@link #stopNewConnections()} call. Connection
+ * process is put on hold after this and {@link SessionManager#connect()} starts
+ * to throw {@link IllegalStateException}. Later {@link #closeSession()} must
+ * be called possibly asynchronously. After this the resource is available again
+ * and a new session may be created.
+ * <li>Do not call any of methods listed above. This probably works but is
+ * not specified here.
+ * </ol>
+ */
+ protected abstract void lastTicketDismissed();
+
+ /**
+ * See {@link #lastTicketDismissed()}. This method is supposed to be called
+ * from there, but not necessarily.
+ */
+ protected void stopNewConnections() {
+ synchronized (manager) {
+ isConnectionStopped = true;
+ }
+ }
+
+ /**
+ * Stops all new connections and cancels all existing tickets. Don't forget
+ * to call {@link #closeSession()} manually.
+ * @return collection of exceptions we gathered from tickets
+ */
+ protected Collection<? extends RuntimeException> interruptSession() {
+ synchronized (manager) {
+ isConnectionStopped = true;
+ isCancelled = true;
+ // TODO(peter.rybin): notify listeners here in case they are interested
+ tickets.clear();
+ }
+
+ return Collections.emptyList();
+ }
+
+ /**
+ * See {@link #lastTicketDismissed()}. This method is supposed to be called
+ * from there, but not necessarily.
+ */
+ protected void closeSession() {
+ synchronized (manager) {
+ isConnectionStopped = true;
+ if (!tickets.isEmpty()) {
+ throw new IllegalStateException("Some tickets are still valid");
+ }
+ if (manager.currentSession != this) {
+ throw new IllegalStateException("Session is not active");
+ }
+ manager.currentSession = null;
+ }
+ }
+
+ /**
+ * Creates new ticket that is to be dismissed later.
+ * Internal method. However user may use it or even make it public.
+ */
+ protected Ticket<SESSION> newTicket() {
+ synchronized (manager) {
+ if (isConnectionStopped) {
+ throw new IllegalStateException("Connection has been stopped");
+ }
+ TicketImpl ticketImpl = new TicketImpl();
+ tickets.add(ticketImpl);
+ return ticketImpl;
+ }
+ }
+
+ private final List<TicketImpl> tickets = new ArrayList<TicketImpl>();
+
+ private class TicketImpl implements Ticket<SESSION> {
+ private volatile boolean isDismissed = false;
+
+ public void dismiss() {
+ synchronized (manager) {
+ if (!isCancelled) {
+ boolean res = tickets.remove(this);
+ if (!res) {
+ throw new IllegalStateException("Ticket is already dismissed");
+ }
+ if (tickets.isEmpty()) {
+ lastTicketDismissed();
+ }
+ }
+ isDismissed = true;
+ }
+ }
+
+ public SESSION getSession() {
+ if (isDismissed) {
+ throw new IllegalStateException("Ticket is dismissed");
+ }
+ return getThisAsSession();
+ }
+ }
+ }
+
+ /**
+ * This method is completely unsynchronized. Is should be used for
+ * single-threaded tests only.
+ */
+ public SESSION getCurrentSessionForTest() {
+ return currentSession;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/SocketConnectionFactory.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,47 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.io.IOException;
+import java.net.SocketAddress;
+
+import org.chromium.sdk.ConnectionLogger;
+import org.chromium.sdk.internal.transport.Handshaker;
+import org.chromium.sdk.internal.transport.SocketConnection;
+import org.chromium.sdk.internal.transport.Connection.NetListener;
+
+/**
+ * Factory for socket connections. Extremely simple and straight-forward
+ * implementation. Note that it works only with stateless {@link Handshaker}s
+ * because they are reused for every connection.
+ */
+public class SocketConnectionFactory implements ConnectionFactory {
+ private final SocketAddress endpoint;
+ private final int connectionTimeoutMs;
+ private final ConnectionLogger.Factory connectionLoggerFactory;
+ private final Handshaker handshaker;
+
+ public SocketConnectionFactory(SocketAddress endpoint, int connectionTimeoutMs,
+ ConnectionLogger.Factory connectionLoggerFactory, Handshaker handshaker) {
+ this.endpoint = endpoint;
+ this.connectionTimeoutMs = connectionTimeoutMs;
+ this.connectionLoggerFactory = connectionLoggerFactory;
+ this.handshaker = handshaker;
+ }
+
+ public SocketConnection newOpenConnection(NetListener netListener) throws IOException {
+ ConnectionLogger connectionLogger;
+ if (connectionLoggerFactory == null) {
+ connectionLogger = null;
+ } else {
+ connectionLogger = connectionLoggerFactory.newConnectionLogger();
+ }
+ SocketConnection connection = new SocketConnection(endpoint, connectionTimeoutMs,
+ connectionLogger, handshaker);
+ connection.setNetListener(netListener);
+ connection.start();
+ return connection;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/StandaloneVmImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,245 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.chromium.sdk.DebugEventListener;
+import org.chromium.sdk.StandaloneVm;
+import org.chromium.sdk.UnsupportedVersionException;
+import org.chromium.sdk.internal.protocol.data.ContextHandle;
+import org.chromium.sdk.internal.tools.v8.V8CommandOutput;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage;
+import org.chromium.sdk.internal.transport.Connection;
+import org.chromium.sdk.internal.transport.Handshaker;
+import org.chromium.sdk.internal.transport.Message;
+import org.chromium.sdk.internal.transport.SocketConnection;
+import org.chromium.sdk.internal.transport.Connection.NetListener;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.ParseException;
+
+/**
+ * Implementation of {@code StandaloneVm}. Currently knows nothing about
+ * contexts, so all existing V8 contexts are presented mixed together.
+ */
+class StandaloneVmImpl extends JavascriptVmImpl implements StandaloneVm {
+
+ /** The class logger. */
+ private static final Logger LOGGER =
+ Logger.getLogger(StandaloneVmImpl.class.getName());
+
+ private static final int WAIT_FOR_HANDSHAKE_TIMEOUT_MS = 3000;
+
+ private static final V8ContextFilter CONTEXT_FILTER = new V8ContextFilter() {
+ public boolean isContextOurs(ContextHandle contextHandle) {
+ // We do not check context in standalone V8 mode.
+ return true;
+ }
+ };
+
+ private final SocketConnection connection;
+ private final Handshaker.StandaloneV8 handshaker;
+
+ private final DebugSession debugSession;
+
+ private DebugEventListener debugEventListener = null;
+ private volatile ConnectionState connectionState = ConnectionState.INIT;
+
+ private volatile Exception disconnectReason = null;
+ private volatile Handshaker.StandaloneV8.RemoteInfo savedRemoteInfo = NULL_REMOTE_INFO;
+
+ private final Object disconnectMonitor = new Object();
+
+ StandaloneVmImpl(SocketConnection connection, Handshaker.StandaloneV8 handshaker) {
+ this.connection = connection;
+ this.handshaker = handshaker;
+ V8CommandOutputImpl v8CommandOutput = new V8CommandOutputImpl(connection);
+ this.debugSession = new DebugSession(sessionManager, CONTEXT_FILTER, v8CommandOutput);
+ }
+
+ public void attach(DebugEventListener listener) throws IOException, UnsupportedVersionException {
+ Exception errorCause = null;
+ try {
+ attachImpl(listener);
+ } catch (IOException e) {
+ errorCause = e;
+ throw e;
+ } catch (UnsupportedVersionException e) {
+ errorCause = e;
+ throw e;
+ } finally {
+ if (errorCause != null) {
+ disconnectReason = errorCause;
+ connectionState = ConnectionState.DETACHED;
+ connection.close();
+ }
+ }
+ }
+
+ private void attachImpl(DebugEventListener listener) throws IOException,
+ UnsupportedVersionException {
+ connectionState = ConnectionState.CONNECTING;
+
+ NetListener netListener = new NetListener() {
+ public void connectionClosed() {
+ }
+
+ public void eosReceived() {
+ debugSession.getV8CommandProcessor().processEos();
+ onDebuggerDetachedImpl(null);
+ }
+
+ public void messageReceived(Message message) {
+ JSONObject json;
+ try {
+ json = JsonUtil.jsonObjectFromJson(message.getContent());
+ } catch (ParseException e) {
+ LOGGER.log(Level.SEVERE, "Invalid JSON received: {0}", message.getContent());
+ return;
+ }
+ debugSession.getV8CommandProcessor().processIncomingJson(json);
+ }
+ };
+ connection.setNetListener(netListener);
+
+ connection.start();
+
+ connectionState = ConnectionState.EXPECTING_HANDSHAKE;
+
+ Handshaker.StandaloneV8.RemoteInfo remoteInfo;
+ try {
+ remoteInfo = handshaker.getRemoteInfo().get(WAIT_FOR_HANDSHAKE_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (ExecutionException e) {
+ throw new IOException("Failed to get version", e);
+ } catch (TimeoutException e) {
+ throw new IOException("Timed out waiting for version", e);
+ }
+
+ String versionString = remoteInfo.getProtocolVersion();
+ // TODO(peter.rybin): check version here
+ if (versionString == null) {
+ throw new UnsupportedVersionException(null, null);
+ }
+
+ StandaloneVmImpl.this.savedRemoteInfo = remoteInfo;
+
+ StandaloneVmImpl.this.debugEventListener = listener;
+
+ debugSession.startCommunication();
+
+ connectionState = ConnectionState.CONNECTED;
+ }
+
+ public boolean detach() {
+ boolean res = onDebuggerDetachedImpl(null);
+ if (!res) {
+ return false;
+ }
+ connection.close();
+ return true;
+ }
+
+ public boolean isAttached() {
+ return connectionState == ConnectionState.CONNECTED;
+ }
+
+ private boolean onDebuggerDetachedImpl(Exception cause) {
+ synchronized (disconnectMonitor) {
+ if (!isAttached()) {
+ // We've already been notified.
+ return false;
+ }
+ connectionState = ConnectionState.DETACHED;
+ disconnectReason = cause;
+ }
+ if (debugEventListener != null) {
+ debugEventListener.disconnected();
+ }
+ return true;
+ }
+
+ @Override
+ protected DebugSession getDebugSession() {
+ return debugSession;
+ }
+
+ /**
+ * @return name of embedding application as it wished to name itself; might be null
+ */
+ public String getEmbedderName() {
+ return savedRemoteInfo.getEmbeddingHostName();
+ }
+
+ /**
+ * @return version of V8 implementation, format is unspecified; not null
+ */
+ public String getVmVersion() {
+ return savedRemoteInfo.getV8VmVersion();
+ }
+
+ public String getDisconnectReason() {
+ // Save volatile field in local variable.
+ Exception cause = disconnectReason;
+ if (cause == null) {
+ return null;
+ }
+ return cause.getMessage();
+ }
+
+ private final DebugSessionManager sessionManager = new DebugSessionManager() {
+ public DebugEventListener getDebugEventListener() {
+ return debugEventListener;
+ }
+
+ public void onDebuggerDetached() {
+ // Never called for standalone.
+ }
+ };
+
+ private final static Handshaker.StandaloneV8.RemoteInfo NULL_REMOTE_INFO =
+ new Handshaker.StandaloneV8.RemoteInfo() {
+ public String getEmbeddingHostName() {
+ return null;
+ }
+ public String getProtocolVersion() {
+ return null;
+ }
+ public String getV8VmVersion() {
+ return null;
+ }
+ };
+
+ private enum ConnectionState {
+ INIT,
+ CONNECTING,
+ EXPECTING_HANDSHAKE,
+ CONNECTED,
+ DETACHED
+ }
+
+ private static class V8CommandOutputImpl implements V8CommandOutput {
+ private final Connection outputConnection;
+
+ V8CommandOutputImpl(Connection outputConnection) {
+ this.outputConnection = outputConnection;
+ }
+ public void send(DebuggerMessage debuggerMessage, boolean immediate) {
+ String jsonString = JsonUtil.streamAwareToJson(debuggerMessage);
+ Message message = new Message(Collections.<String, String>emptyMap(), jsonString);
+
+ outputConnection.send(message);
+ // TODO(peter.rybin): support {@code immediate} in protocol
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/SubpropertiesMirror.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,155 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.chromium.sdk.internal.protocol.data.FunctionValueHandle;
+import org.chromium.sdk.internal.protocol.data.ObjectValueHandle;
+import org.chromium.sdk.internal.tools.v8.V8ProtocolUtil;
+
+/**
+ * This class is intended to hold properties either already parsed or to be parsed on demand.
+ */
+public abstract class SubpropertiesMirror {
+ public abstract List<? extends PropertyReference> getProperties();
+
+ public abstract List<? extends PropertyReference> getInternalProperties();
+
+ public abstract Object getAdditionalProperties();
+
+ public static class ObjectValueBased extends JsonBased<ObjectValueHandle> {
+ private final ObjectValueHandle objectValueHandle;
+ public ObjectValueBased(ObjectValueHandle valueHandle,
+ AdditionalPropertyFactory<ObjectValueHandle> additionalPropertyFactory) {
+ super(additionalPropertyFactory);
+ this.objectValueHandle = valueHandle;
+ }
+ @Override
+ protected ObjectValueHandle getObjectForFactory() {
+ return objectValueHandle;
+ }
+ @Override
+ protected ObjectValueHandle getObjectValue() {
+ return objectValueHandle;
+ }
+ }
+ public static class FunctionValueBased extends JsonBased<FunctionValueHandle> {
+ private final FunctionValueHandle functionValueHandle;
+ public FunctionValueBased(FunctionValueHandle functionValueHandle,
+ AdditionalPropertyFactory<FunctionValueHandle> additionalPropertyFactory) {
+ super(additionalPropertyFactory);
+ this.functionValueHandle = functionValueHandle;
+ }
+ @Override
+ protected FunctionValueHandle getObjectForFactory() {
+ return functionValueHandle;
+ }
+ @Override
+ protected ObjectValueHandle getObjectValue() {
+ return functionValueHandle.getSuper();
+ }
+ }
+
+ /**
+ * Keeps properties in for of JSON and parses JSON on demand.
+ */
+ public static abstract class JsonBased<T> extends SubpropertiesMirror {
+ private final AdditionalPropertyFactory<T> additionalPropertyFactory;
+
+ private List<? extends PropertyReference> properties = null;
+ private List<? extends PropertyReference> internalProperties = null;
+ private Object additionalProperties = null;
+
+ public JsonBased(AdditionalPropertyFactory<T> additionalPropertyFactory) {
+ if (additionalPropertyFactory == null) {
+ additionalPropertyFactory = NO_OP_FACTORY;
+ }
+ this.additionalPropertyFactory = additionalPropertyFactory;
+ }
+
+ @Override
+ public synchronized List<? extends PropertyReference> getProperties() {
+ if (properties == null) {
+ properties = V8ProtocolUtil.extractObjectProperties(getObjectValue());
+ }
+ return properties;
+ }
+
+ @Override
+ public synchronized List<? extends PropertyReference> getInternalProperties() {
+ if (internalProperties == null) {
+ internalProperties = V8ProtocolUtil.extractObjectInternalProperties(getObjectValue());
+ }
+ return internalProperties;
+ }
+
+ protected abstract ObjectValueHandle getObjectValue();
+
+ @Override
+ public Object getAdditionalProperties() {
+ if (additionalProperties == null) {
+ additionalProperties =
+ additionalPropertyFactory.createAdditionalProperties(getObjectForFactory());
+ }
+ return additionalProperties;
+ }
+ protected abstract T getObjectForFactory();
+
+ public interface AdditionalPropertyFactory<T> {
+ Object createAdditionalProperties(T jsonWithProperties);
+ }
+
+ private static AdditionalPropertyFactory NO_OP_FACTORY = new AdditionalPropertyFactory<Void>() {
+ public Object createAdditionalProperties(Void jsonWithProperties) {
+ return EMPTY_OBJECT;
+ }
+ };
+ }
+
+ static class ListBased extends SubpropertiesMirror {
+ private final List<PropertyReference> list;
+
+ ListBased(PropertyReference ... refs) {
+ this.list = Collections.unmodifiableList(Arrays.asList(refs));
+ }
+
+ @Override
+ public List<? extends PropertyReference> getProperties() {
+ return list;
+ }
+
+ @Override
+ public List<? extends PropertyReference> getInternalProperties() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Object getAdditionalProperties() {
+ return EMPTY_OBJECT;
+ }
+ }
+
+ static final SubpropertiesMirror EMPTY = new SubpropertiesMirror() {
+ @Override
+ public List<? extends PropertyReference> getProperties() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List<? extends PropertyReference> getInternalProperties() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Object getAdditionalProperties() {
+ return EMPTY_OBJECT;
+ }
+ };
+
+ private static final Object EMPTY_OBJECT = new Object();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/V8ContextFilter.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import org.chromium.sdk.internal.protocol.data.ContextHandle;
+
+/**
+ * Embedder-specific filter for V8 VM contexts.
+ */
+public interface V8ContextFilter {
+ /**
+ * Given a context handler, it should check whether it is our context or not.
+ * The field {@link ContextHandle#data()} of embedder-specific type should be used.
+ * @return whether the context is ours
+ */
+ boolean isContextOurs(ContextHandle contextHandle);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/V8VersionMilestones.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,18 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import org.chromium.sdk.Version;
+
+/**
+ * Stores milestone version numbers that marks when a particular feature was implemented.
+ */
+public class V8VersionMilestones {
+ private final static Version ACCURATE_RUNNING_FIELD = new Version(1, 3, 16);
+
+ public static boolean isRunningAccurate(Version vmVersion) {
+ return vmVersion != null && ACCURATE_RUNNING_FIELD.compareTo(vmVersion) <= 0;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/ValueLoadException.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+/**
+ * Signals a problem in loading value from remote, most probably a parsing problem.
+ */
+public class ValueLoadException extends RuntimeException {
+
+ public ValueLoadException() {
+ }
+
+ public ValueLoadException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ValueLoadException(String message) {
+ super(message);
+ }
+
+ public ValueLoadException(Throwable cause) {
+ super(cause);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/ValueLoader.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,258 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.chromium.sdk.internal.InternalContext.ContextDismissedCheckedException;
+import org.chromium.sdk.internal.protocol.ScopeBody;
+import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
+import org.chromium.sdk.internal.protocol.data.ObjectValueHandle;
+import org.chromium.sdk.internal.protocol.data.SomeHandle;
+import org.chromium.sdk.internal.protocol.data.ValueHandle;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.v8.V8BlockingCallback;
+import org.chromium.sdk.internal.tools.v8.V8Helper;
+import org.chromium.sdk.internal.tools.v8.V8ProtocolUtil;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory;
+import org.json.simple.JSONObject;
+
+/**
+ * The elaborate factory for {@link ValueMirror}'s, that loads values from remote and
+ * caches them. All the data comes originally in form of JSON strings which may contain
+ * less or more fields, so it creates {@link ValueMirror} or {@link PropertyHoldingValueMirror}
+ * accordingly.
+ */
+public class ValueLoader {
+ private final ConcurrentMap<Long, ValueMirror> refToMirror =
+ new ConcurrentHashMap<Long, ValueMirror>();
+ private final InternalContext context;
+
+ ValueLoader(InternalContext context) {
+ this.context = context;
+ }
+
+ /**
+ * Receives {@link ValueMirror} and makes sure it has its properties loaded.
+ */
+ public PropertyHoldingValueMirror loadSubpropertiesInMirror(ValueMirror mirror) {
+ PropertyHoldingValueMirror references = mirror.getProperties();
+ if (references == null) {
+ // need to look up this value again
+ List<PropertyHoldingValueMirror> loadedMirrors =
+ loadValuesFromRemote(Collections.singletonList(Long.valueOf(mirror.getRef())));
+ references = loadedMirrors.get(0);
+ }
+ return references;
+ }
+
+ /**
+ * Looks up data for scope on remote.
+ */
+ public List<? extends PropertyReference> loadScopeFields(int scopeNumber, int frameNumber) {
+ DebuggerMessage message = DebuggerMessageFactory.scope(scopeNumber, frameNumber);
+
+ V8BlockingCallback<List<? extends PropertyReference>> callback =
+ new V8BlockingCallback<List<? extends PropertyReference>>() {
+ @Override
+ protected List<? extends PropertyReference> handleSuccessfulResponse(
+ SuccessCommandResponse response) {
+ return readFromScopeResponse(response);
+ }
+ };
+
+ try {
+ return V8Helper.callV8Sync(context, message, callback);
+ } catch (ContextDismissedCheckedException e) {
+ context.getDebugSession().maybeRethrowContextException(e);
+ // or
+ return Collections.emptyList();
+ }
+ }
+
+ private List<? extends PropertyReference> readFromScopeResponse(SuccessCommandResponse response) {
+ List<SomeHandle> refs = response.getRefs();
+
+ HandleManager handleManager = context.getHandleManager();
+ for (int i = 0; i < refs.size(); i++) {
+ SomeHandle ref = refs.get(i);
+ handleManager.put(ref);
+ }
+ ScopeBody body;
+ try {
+ body = response.getBody().asScopeBody();
+ } catch (JsonProtocolParseException e) {
+ throw new ValueLoadException(e);
+ }
+ ObjectValueHandle objectRef = body.getObject();
+ return V8ProtocolUtil.extractObjectProperties(objectRef);
+ }
+
+/**
+ * For each PropertyReference from propertyRefs tries to either: 1. read it from PropertyReference
+ * (possibly cached value) or 2. lookup value by refId from remote
+ */
+ public List<ValueMirror> getOrLoadValueFromRefs(List<? extends PropertyReference> propertyRefs) {
+ ValueMirror[] result = new ValueMirror[propertyRefs.size()];
+ List<Integer> mapForLoadResults = new ArrayList<Integer>();
+ List<PropertyReference> needsLoading = new ArrayList<PropertyReference>();
+
+ for (int i = 0; i < propertyRefs.size(); i++) {
+ PropertyReference ref = propertyRefs.get(i);
+ ValueMirror mirror = readFromPropertyReference(ref);
+ if (mirror == null) {
+ // We don't have the data (enough) right now. We are requesting them from server.
+ // There might be simultaneous request for the same value, which is a normal though
+ // undesired case.
+ needsLoading.add(ref);
+ mapForLoadResults.add(i);
+ }
+ result[i] = mirror;
+ }
+
+ List<Long> refIds = getRefIdFromReferences(needsLoading);
+ List<PropertyHoldingValueMirror> loadedMirrors = loadValuesFromRemote(refIds);
+ assert refIds.size() == loadedMirrors.size();
+ for (int i = 0; i < loadedMirrors.size(); i++) {
+ int pos = mapForLoadResults.get(i);
+ result[pos] = loadedMirrors.get(i).getValueMirror();
+ }
+ return Arrays.asList(result);
+ }
+
+ private static List<Long> getRefIdFromReferences(final List<PropertyReference> propertyRefs) {
+ List<Long> result = new ArrayList<Long>(propertyRefs.size());
+ for (PropertyReference ref : propertyRefs) {
+ result.add(Long.valueOf(ref.getRef()));
+ }
+ return result;
+ }
+
+ /**
+ * Reads data from caches or from JSON from propertyReference. Never accesses remote.
+ */
+ private ValueMirror readFromPropertyReference(PropertyReference propertyReference) {
+ Long refIdObject = propertyReference.getRef();
+
+ ValueMirror mirror = refToMirror.get(refIdObject);
+ if (mirror != null) {
+ return mirror;
+ }
+ SomeHandle cachedHandle = context.getHandleManager().getHandle(refIdObject);
+ // If we have cached handle, we reads cached handle, not using one from propertyeReference
+ // because we expect to find more complete version in cache. Is it ok?
+ if (cachedHandle != null) {
+ ValueHandle valueHandle;
+ try {
+ valueHandle = cachedHandle.asValueHandle();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ mirror = V8Helper.createValueMirrorOptional(valueHandle);
+ } else {
+ DataWithRef handleFromProperty = propertyReference.getValueObject();
+
+ mirror = V8Helper.createValueMirrorOptional(handleFromProperty);
+ }
+ if (mirror != null) {
+ ValueMirror mirror2 = refToMirror.putIfAbsent(refIdObject, mirror);
+ if (mirror2 != null) {
+ mergeMirrors(mirror2, mirror);
+ }
+ }
+
+ return mirror;
+ }
+
+ /**
+ * Requests values from remote via "lookup" command. Automatically caches JSON objects
+ * in {@link HandleManager}.
+ * @param propertyRefIds list of ref ids we need to look up
+ * @return loaded value mirrors in the same order as in propertyRefIds
+ */
+ public List<PropertyHoldingValueMirror> loadValuesFromRemote(final List<Long> propertyRefIds) {
+ if (propertyRefIds.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ DebuggerMessage message = DebuggerMessageFactory.lookup(propertyRefIds, false);
+
+ V8BlockingCallback<List<PropertyHoldingValueMirror>> callback =
+ new V8BlockingCallback<List<PropertyHoldingValueMirror>>() {
+ @Override
+ protected List<PropertyHoldingValueMirror> handleSuccessfulResponse(
+ SuccessCommandResponse response) {
+ return readResponseFromLookup(response, propertyRefIds);
+ }
+ };
+
+ try {
+ return V8Helper.callV8Sync(context, message, callback);
+ } catch (ContextDismissedCheckedException e) {
+ context.getDebugSession().maybeRethrowContextException(e);
+ // or
+ throw new ValueLoadException("Invalid context", e);
+ }
+ }
+
+ private List<PropertyHoldingValueMirror> readResponseFromLookup(
+ SuccessCommandResponse successResponse, List<Long> propertyRefIds) {
+ List<PropertyHoldingValueMirror> result =
+ new ArrayList<PropertyHoldingValueMirror>(propertyRefIds.size());
+ JSONObject body;
+ try {
+ body = successResponse.getBody().asLookupMap();
+ } catch (JsonProtocolParseException e) {
+ throw new ValueLoadException(e);
+ }
+ for (int i = 0; i < propertyRefIds.size(); i++) {
+ int ref = propertyRefIds.get(i).intValue();
+ JSONObject value = JsonUtil.getAsJSON(body, String.valueOf(ref));
+ if (value == null) {
+ throw new ValueLoadException("Failed to find value for ref=" + ref);
+ }
+ SomeHandle smthHandle = context.getHandleManager().put((long)ref, value);
+ ValueHandle valueHandle;
+ try {
+ valueHandle = smthHandle.asValueHandle();
+ } catch (JsonProtocolParseException e) {
+ throw new ValueLoadException(e);
+ }
+
+ result.add(readMirrorFromLookup(ref, valueHandle));
+ }
+ return result;
+ }
+
+ /**
+ * Constructs a ValueMirror given a V8 debugger object specification and the
+ * value name.
+ *
+ * @param jsonValue containing the object specification from the V8 debugger
+ * @param ref
+ * @return a ValueMirror instance with the specified name, containing data
+ * from handle, or {@code null} if {@code handle} is not a handle
+ */
+ private PropertyHoldingValueMirror readMirrorFromLookup(int ref, ValueHandle jsonValue) {
+ PropertyHoldingValueMirror propertiesMirror = V8Helper.createMirrorFromLookup(jsonValue);
+ ValueMirror newMirror = propertiesMirror.getValueMirror();
+
+ ValueMirror oldMirror = refToMirror.putIfAbsent((long)ref, newMirror);
+ if (oldMirror != null) {
+ mergeMirrors(oldMirror, newMirror);
+ }
+ return propertiesMirror;
+ }
+
+ private static void mergeMirrors(ValueMirror baseMirror, ValueMirror alternativeMirror) {
+ baseMirror.mergeFrom(alternativeMirror);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/ValueMirror.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,151 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal;
+
+import org.chromium.sdk.JsValue.Type;
+
+/**
+ * A representation of a datum (value) in the remote JavaScript VM. Must contain all the
+ * data immutable, except for properties. Reference to properties is optional and may be set later.
+ */
+public class ValueMirror {
+
+ public static PropertyHoldingValueMirror createScalar(String value, Type type, String className) {
+ return new ValueMirror(value, type, className).getProperties();
+ }
+
+ public static PropertyHoldingValueMirror createObject(int refID,
+ SubpropertiesMirror subpropertiesMirror, Type type, String className) {
+ if (subpropertiesMirror == null) {
+ throw new NullPointerException();
+ }
+ return new ValueMirror(refID, subpropertiesMirror, type, className).getProperties();
+ }
+
+ public static ValueMirror createObjectUnknownProperties(int refID, Type type, String className) {
+ return new ValueMirror(refID, null, type, className);
+ }
+
+ private final int ref;
+
+ private final Type type;
+
+ private final String value;
+
+ private final String className;
+
+ private volatile PropertyHoldingValueMirror properties = null;
+
+ private ValueMirror(String value, Type type, String className) {
+ this.type = type;
+ this.value = value;
+ this.ref = -1;
+ this.className = className;
+ this.properties = new PropertyHoldingValueMirror(this);
+ }
+
+ private ValueMirror(int refID, SubpropertiesMirror subpropertiesMirror, Type type,
+ String className) {
+ this.type = type;
+ this.className = className;
+ this.ref = refID;
+ PropertyHoldingValueMirror propertiesMirror;
+ if (subpropertiesMirror == null) {
+ propertiesMirror = null;
+ } else {
+ propertiesMirror = new PropertyHoldingValueMirror(this, subpropertiesMirror);
+ }
+ this.properties = propertiesMirror;
+ this.value = null;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public PropertyHoldingValueMirror getProperties() {
+ return properties;
+ }
+
+ public int getRef() {
+ return ref;
+ }
+
+ /**
+ * @return the type representation as a String
+ */
+ public String getTypeAsString() {
+ switch (type) {
+ case TYPE_NUMBER:
+ case TYPE_OBJECT:
+ case TYPE_ARRAY:
+ case TYPE_FUNCTION:
+ case TYPE_DATE:
+ return JsDataTypeUtil.getJsonString(type);
+ case TYPE_STRING:
+ default:
+ return "text";
+ }
+ }
+
+ @Override
+ public String toString() {
+ switch (type) {
+ case TYPE_UNDEFINED:
+ case TYPE_NULL:
+ case TYPE_DATE:
+ case TYPE_STRING:
+ case TYPE_NUMBER:
+ case TYPE_BOOLEAN:
+ case TYPE_REGEXP:
+ return value == null
+ ? ""
+ : value;
+ case TYPE_OBJECT:
+ case TYPE_ARRAY:
+ return "[" + className + "]";
+ case TYPE_FUNCTION:
+ return "[Function]";
+ default:
+ return "";
+ }
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ private static Type getObjectJsType(String className) {
+ return JsDataTypeUtil.fromJsonTypeAndClassName("object", className);
+ }
+
+ void mergeFrom(ValueMirror alternative) {
+ synchronized (MERGE_VALUE_MIRROR_MONITOR) {
+ if (alternative.properties != null) {
+ if (this.properties == null) {
+ this.properties =
+ new PropertyHoldingValueMirror(this, alternative.properties.getSubpropertiesMirror());
+ } else {
+ mergeProperties(this.properties, alternative.properties);
+ }
+ }
+ }
+ }
+
+ private static final Object MERGE_VALUE_MIRROR_MONITOR = new Object();
+
+ /**
+ * Merge a record from data base with a new record just received. Theoretically
+ * the new record may have something that base version lacks.
+ * However,this method is more of symbolic use right now.
+ *
+ * @param baseProperties record version which is kept in database
+ * @param altProperties record version that just has come from outside and may
+ * contain additional data
+ */
+ private static void mergeProperties(PropertyHoldingValueMirror baseProperties,
+ PropertyHoldingValueMirror altProperties) {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/AfterCompileBody.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,14 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import org.chromium.sdk.internal.protocol.data.ScriptHandle;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+@JsonType
+public interface AfterCompileBody extends JsonSubtype<EventNotificationBody> {
+ ScriptHandle getScript();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/BacktraceCommandBody.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,26 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import java.util.List;
+
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+@JsonType
+public interface BacktraceCommandBody extends JsonSubtype<CommandResponseBody> {
+
+ List<FrameObject> getFrames();
+
+ @JsonOptionalField
+ Long fromFrame();
+
+ @JsonOptionalField
+ Long toFrame();
+
+ @JsonOptionalField
+ Long totalFrames();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/BreakEventBody.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,43 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import java.util.List;
+
+import org.chromium.sdk.internal.protocol.data.ValueHandle;
+import org.chromium.sdk.internal.protocolparser.JsonField;
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+import org.json.simple.JSONObject;
+
+@JsonType
+public interface BreakEventBody extends JsonSubtype<EventNotificationBody> {
+
+ @JsonOptionalField
+ List<Long> getBreakpoints();
+
+ @JsonOptionalField
+ ValueHandle getException();
+
+ @JsonOptionalField
+ String getSourceLineText();
+
+ @JsonOptionalField
+ @JsonField(jsonLiteralName="uncaught")
+ Boolean isUncaught();
+
+ @JsonOptionalField
+ Long getSourceLine();
+
+ @JsonOptionalField
+ String getInvocationText();
+
+ @JsonOptionalField
+ JSONObject getScript();
+
+ @JsonOptionalField
+ Long getSourceColumn();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/BreakpointBody.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import org.chromium.sdk.internal.protocolparser.JsonNullable;
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+@JsonType
+public interface BreakpointBody extends JsonSubtype<CommandResponseBody> {
+
+ long getBreakpoint();
+
+ @JsonOptionalField
+ @JsonNullable
+ Object column();
+
+ @JsonOptionalField
+ Long line();
+
+ @JsonOptionalField
+ String script_name();
+
+ @JsonOptionalField
+ String type();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/CommandResponse.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,49 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import java.util.EnumSet;
+
+import org.chromium.sdk.internal.protocolparser.EnumValueCondition;
+import org.chromium.sdk.internal.protocolparser.JsonField;
+import org.chromium.sdk.internal.protocolparser.JsonOverrideField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCasting;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeConditionCustom;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+/**
+ * A generic type for all command responses. There are 2 subtypes; one for
+ * success responses and one for failure responses.
+ */
+@JsonType
+public interface CommandResponse extends JsonSubtype<IncomingMessage> {
+
+ @JsonOverrideField
+ @JsonSubtypeConditionCustom(condition=TypeValueCondition.class)
+ MessageType getType();
+
+ class TypeValueCondition extends EnumValueCondition<MessageType> {
+ public TypeValueCondition() {
+ super(EnumSet.of(MessageType.response));
+ }
+ }
+
+ /**
+ * Id of the corresponding request sent to debugger.
+ */
+ @JsonField(jsonLiteralName="request_seq")
+ long getRequestSeq();
+
+ String getCommand();
+
+ boolean success();
+
+ @JsonSubtypeCasting
+ SuccessCommandResponse asSuccess();
+
+ @JsonSubtypeCasting
+ FailedCommandResponse asFailure();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/CommandResponseBody.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,44 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import java.util.List;
+
+import org.chromium.sdk.internal.protocol.data.ScriptHandle;
+import org.chromium.sdk.internal.protocol.data.ValueHandle;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCasting;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+import org.json.simple.JSONObject;
+
+/**
+ * This is empty base type for all command response body types. The actual type
+ * depends on a particular command. Note that in JSON sometimes it is an array rather than object
+ * (for scripts).
+ */
+@JsonType(subtypesChosenManually=true)
+public interface CommandResponseBody {
+ @JsonSubtypeCasting
+ BacktraceCommandBody asBacktraceCommandBody() throws JsonProtocolParseException;
+
+ @JsonSubtypeCasting
+ List<ScriptHandle> asScripts() throws JsonProtocolParseException;
+
+ @JsonSubtypeCasting
+ BreakpointBody asBreakpointBody() throws JsonProtocolParseException;
+
+ @JsonSubtypeCasting
+ // map refId -> ValueHandle
+ JSONObject asLookupMap() throws JsonProtocolParseException;
+
+ @JsonSubtypeCasting(reinterpret=true)
+ ValueHandle asEvaluateBody() throws JsonProtocolParseException;
+
+ @JsonSubtypeCasting
+ ScopeBody asScopeBody() throws JsonProtocolParseException;
+
+ @JsonSubtypeCasting
+ VersionBody asVersionBody() throws JsonProtocolParseException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/EventNotification.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,46 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import java.util.EnumSet;
+import java.util.List;
+
+import org.chromium.sdk.internal.protocol.data.SomeHandle;
+import org.chromium.sdk.internal.protocolparser.EnumValueCondition;
+import org.chromium.sdk.internal.protocolparser.JsonObjectBased;
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonOverrideField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeConditionCustom;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+import org.json.simple.JSONObject;
+
+/**
+ * A type for event notification message. Its structure is similar
+ * to {@link SuccessCommandResponse}.
+ */
+@JsonType
+public interface EventNotification extends JsonObjectBased, JsonSubtype<IncomingMessage> {
+ @JsonOverrideField
+ @JsonSubtypeConditionCustom(condition=TypeValueCondition.class)
+ MessageType getType();
+
+ class TypeValueCondition extends EnumValueCondition<MessageType> {
+ public TypeValueCondition() {
+ super(EnumSet.of(MessageType.event));
+ }
+ }
+
+ String getEvent();
+
+ EventNotificationBody getBody();
+
+ // TODO(peter.rybin): does this field really exist?
+ @JsonOptionalField
+ JSONObject getException();
+
+ @JsonOptionalField
+ List<SomeHandle> getRefs();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/EventNotificationBody.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,22 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCasting;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+/**
+ * This is empty base type for all event notification body types. The actual type
+ * depends on a particular event.
+ */
+@JsonType(subtypesChosenManually=true)
+public interface EventNotificationBody {
+ @JsonSubtypeCasting
+ BreakEventBody asBreakEventBody() throws JsonProtocolParseException;
+
+ @JsonSubtypeCasting
+ AfterCompileBody asAfterCompileBody() throws JsonProtocolParseException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/FailedCommandResponse.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,31 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import org.chromium.sdk.internal.protocolparser.JsonField;
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonOverrideField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeConditionBoolValue;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+/**
+ * A type for failed command response message. It should contain "message" field
+ * hinting at the cause of the failure.
+ */
+@JsonType
+public interface FailedCommandResponse extends JsonSubtype<CommandResponse> {
+ @JsonOverrideField
+ @JsonSubtypeConditionBoolValue(false)
+ boolean success();
+
+ String getMessage();
+
+ @JsonField(jsonLiteralName="request_seq")
+ Long getRequestSeq();
+
+ @JsonOptionalField
+ String getCommand();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/FrameObject.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,56 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import java.util.List;
+
+import org.chromium.sdk.internal.protocol.data.PropertyObject;
+import org.chromium.sdk.internal.protocol.data.SomeHandle;
+import org.chromium.sdk.internal.protocol.data.SomeRef;
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+import org.json.simple.JSONObject;
+
+/**
+ * A frame mirror object type. Technically it is almost subtype of {@link SomeHandle}:
+ * it gets serialized from the same code; however, it never gets handle field so
+ * we have to treat it as a separate type. Hopefully frame object will never
+ * get mixed with other objects on remote side.
+ */
+@JsonType
+public interface FrameObject {
+
+ long getIndex();
+
+ JSONObject getFunc();
+
+ String getText();
+
+ long getLine();
+
+ String getSourceLineText();
+
+ @JsonOptionalField
+ SomeRef getScript();
+
+ List<PropertyObject> getArguments();
+
+ List<PropertyObject> getLocals();
+
+ SomeRef getReceiver();
+
+ List<ScopeRef> getScopes();
+
+ Boolean constructCall();
+
+ String type();
+
+ Long position();
+
+ Long column();
+
+ Boolean debuggerFrame();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/IncomingMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,25 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCasting;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+/**
+ * A base type for all incoming message from debugger. There are 2 kinds of messages: response
+ * to a command and event notification. All messages must have unique sequence id.
+ */
+@JsonType
+public interface IncomingMessage {
+ long getSeq();
+
+ MessageType getType();
+
+ @JsonSubtypeCasting
+ CommandResponse asCommandResponse();
+
+ @JsonSubtypeCasting
+ EventNotification asEventNotification();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/MessageType.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,9 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+public enum MessageType {
+ response, event
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/ScopeBody.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,26 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import org.chromium.sdk.internal.protocol.data.ObjectValueHandle;
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+@JsonType
+public interface ScopeBody extends JsonSubtype<CommandResponseBody> {
+
+ ObjectValueHandle getObject();
+
+ @JsonOptionalField
+ String text();
+
+ long index();
+
+ long frameIndex();
+
+ long type();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/ScopeRef.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,13 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+@JsonType
+public interface ScopeRef {
+ long index();
+ long type();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/SuccessCommandResponse.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,37 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import java.util.List;
+
+import org.chromium.sdk.internal.protocol.data.SomeHandle;
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonOverrideField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeConditionBoolValue;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+/**
+ * A type for success command response message. It holds all the data in
+ * "body" field and usually provides "reference" part with data for all referenced objects.
+ */
+@JsonType
+public interface SuccessCommandResponse extends JsonSubtype<CommandResponse> {
+ @JsonOverrideField
+ @JsonSubtypeConditionBoolValue(true)
+ boolean success();
+
+ @JsonOptionalField
+ CommandResponseBody getBody();
+
+ @JsonOptionalField
+ List<SomeHandle> getRefs();
+
+ /**
+ * @return whether VM continue running after handling the command; however next commands
+ * may change it
+ */
+ boolean running();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/VersionBody.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,17 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol;
+
+import org.chromium.sdk.internal.protocolparser.JsonField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+@JsonType
+public interface VersionBody extends JsonSubtype<CommandResponseBody> {
+
+ @JsonField(jsonLiteralName="V8Version")
+ String getV8Version();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/ContextData.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,13 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol.data;
+
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+@JsonType
+public interface ContextData {
+ long value();
+ String type();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/ContextHandle.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol.data;
+
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+@JsonType
+public interface ContextHandle extends JsonSubtype<SomeHandle> {
+ /**
+ * Any value, provided by V8 embedding application. For Chrome it may be {@link ContextData}
+ * as well as its stringified form (as "type,in").
+ */
+ @JsonOptionalField
+ Object data();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/FunctionValueHandle.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,41 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol.data;
+
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCondition;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+import org.json.simple.JSONObject;
+
+@JsonType
+public interface FunctionValueHandle extends JsonSubtype<ObjectValueHandle> {
+ @JsonOptionalField
+ Long position();
+
+ @JsonOptionalField
+ Long line();
+
+ @JsonOptionalField
+ JSONObject script();
+
+ @JsonSubtypeCondition
+ boolean resolved();
+
+ @JsonOptionalField
+ String source();
+
+ @JsonOptionalField
+ String inferredName();
+
+ @JsonOptionalField
+ String name();
+
+ @JsonOptionalField
+ Long column();
+
+ @JsonOptionalField
+ Long scriptId();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/ObjectValueHandle.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,30 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol.data;
+
+import java.util.List;
+
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCasting;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCondition;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+@JsonType
+public interface ObjectValueHandle extends JsonSubtype<ValueHandle> {
+ @JsonSubtypeCondition
+ List<PropertyObject> properties();
+ SomeRef protoObject();
+ SomeRef constructorFunction();
+
+ @JsonOptionalField
+ SomeRef prototypeObject();
+
+ @JsonSubtypeCasting
+ FunctionValueHandle asFunction();
+
+ @JsonSubtypeCasting
+ void notFunction();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/PropertyObject.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol.data;
+
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCasting;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+/**
+ * A type for a property object. May have 2 different forms (subtypes).
+ * <p>Gets serialized in mirror-delay.js,
+ * JSONProtocolSerializer.prototype.serializeProperty_
+ */
+@JsonType
+public interface PropertyObject {
+ /**
+ * @return either String (normal property) or Long (array element)
+ */
+ Object name();
+
+ @JsonSubtypeCasting
+ PropertyWithValue asPropertyWithValue();
+
+ @JsonSubtypeCasting
+ PropertyWithRef asPropertyWithRef();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/PropertyWithRef.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,25 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol.data;
+
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCondition;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+@JsonType
+public interface PropertyWithRef extends JsonSubtype<PropertyObject> {
+ @JsonSubtypeCondition(fieldIsAbsent=true)
+ @JsonOptionalField
+ Void getValue();
+
+ long ref();
+
+ @JsonOptionalField
+ Object attributes();
+
+ @JsonOptionalField
+ Long propertyType();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/PropertyWithValue.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,17 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol.data;
+
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCondition;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+@JsonType
+public interface PropertyWithValue extends JsonSubtype<PropertyObject> {
+ @JsonSubtypeCondition
+ @JsonOptionalField
+ RefWithDisplayData getValue();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/RefWithDisplayData.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,42 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol.data;
+
+import org.chromium.sdk.internal.protocolparser.JsonNullable;
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonOverrideField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCondition;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+/**
+ * A reference (a pointer) to an object, that prefetches some of its key properties.
+ * <p>Gets serialized in mirror-delay.js,
+ * JSONProtocolSerializer.prototype.serializeReferenceWithDisplayData_
+ */
+@JsonType
+public interface RefWithDisplayData extends JsonSubtype<SomeRef> {
+
+ @JsonOverrideField
+ long ref();
+
+ @JsonSubtypeCondition
+ String type();
+
+ @JsonOptionalField
+ String className();
+
+ @JsonOptionalField
+ @JsonNullable
+ Object value();
+
+
+ // For function.
+ @JsonOptionalField
+ String inferredName();
+
+ @JsonOptionalField
+ Long scriptId();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/ScriptHandle.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,49 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol.data;
+
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+import org.json.simple.JSONObject;
+
+@JsonType
+public interface ScriptHandle extends JsonSubtype<SomeHandle> {
+
+ long id();
+ long lineOffset();
+ long columnOffset();
+ long lineCount();
+
+ @JsonOptionalField
+ Object data();
+
+ // either sourceStart or source
+ @JsonOptionalField
+ String sourceStart();
+
+ @JsonOptionalField
+ String source();
+
+ long sourceLength();
+ long scriptType();
+ long compilationType();
+
+
+ @JsonOptionalField
+ SomeSerialized evalFromScript();
+
+ @JsonOptionalField
+ JSONObject evalFromLocation();
+
+
+ @JsonOptionalField
+ SomeRef context();
+
+ String text();
+
+ @JsonOptionalField
+ String name();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/SomeHandle.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,40 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol.data;
+
+import org.chromium.sdk.internal.protocol.FrameObject;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCasting;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCondition;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+/**
+ * A serialized form of object when it is fully (though shallowly) described. Object always
+ * has a type and a handle. (See {@link FrameObject} as a case that makes it a bit more messy).
+ * <p>Gets serialized in mirror-delay.js,
+ * JSONProtocolSerializer.prototype.serialize_, main part.
+ */
+@JsonType(subtypesChosenManually=true)
+public interface SomeHandle extends JsonSubtype<SomeSerialized> {
+ /**
+ * An integer "handle" of the object. Normally it is unique (for particular suspended-to-resumed
+ * period). Some auxiliary objects may have non-unique handles which should be negative.
+ */
+ @JsonSubtypeCondition
+ long handle();
+
+ String type();
+
+
+ @JsonSubtypeCasting
+ ScriptHandle asScriptHandle() throws JsonProtocolParseException;
+
+ @JsonSubtypeCasting
+ ValueHandle asValueHandle() throws JsonProtocolParseException;
+
+ @JsonSubtypeCasting
+ ContextHandle asContextHandle() throws JsonProtocolParseException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/SomeRef.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,30 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol.data;
+
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCasting;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCondition;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+
+/**
+ * A reference form of object data serialization. Basically it only has one field "ref" that
+ * is "handle" of an object. Using this integer value as a key, all the object data may be
+ * requested (lookup'ed) from debugger. However some additional data may be available via subtype.
+ * <p>Gets serialized in mirror-delay.js,
+ * first part of JSONProtocolSerializer.prototype.serialize_
+ */
+@JsonType
+public interface SomeRef extends JsonSubtype<SomeSerialized> {
+ @JsonSubtypeCondition
+ long ref();
+
+ @JsonSubtypeCasting
+ RefWithDisplayData asWithDisplayData();
+
+ @JsonSubtypeCasting
+ void asJustSomeRef();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/SomeSerialized.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,25 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol.data;
+
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCasting;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+
+/**
+ * A serialized form of object. There may be 2 schemas: reference (like pointer) or full description
+ * called "a handle" hereafter. It appears that it's not always statically known which of schemas
+ * is used in every place; thus it requires a base type like this.
+ * <p>Gets serialized in mirror-delay.js,
+ * JSONProtocolSerializer.prototype.serialize_
+ */
+@JsonType
+public interface SomeSerialized {
+ @JsonSubtypeCasting
+ SomeRef asSomeRef();
+
+ @JsonSubtypeCasting
+ SomeHandle asSmthWithHandle();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/ValueHandle.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,50 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocol.data;
+
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonOverrideField;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCasting;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+
+/**
+ * A serialization of a JavaScript value. May be cast to {@link ObjectValueHandle} if value is
+ * an object.
+ * <p>Gets serialized in mirror-delay.js,
+ * JSONProtocolSerializer.prototype.serialize_, main part
+ */
+@JsonType
+public interface ValueHandle extends JsonSubtype<SomeHandle> {
+ @JsonOverrideField
+ long handle();
+
+ String text();
+
+ @JsonOptionalField
+ Object value();
+
+ @JsonOverrideField
+ String type();
+
+ // for string type (the true length, value field may be truncated)
+ @JsonOptionalField
+ Long length();
+ @JsonOptionalField
+ Long fromIndex();
+ @JsonOptionalField
+ Long toIndex();
+
+ @JsonOptionalField
+ String className();
+
+ @JsonSubtypeCasting
+ ObjectValueHandle asObject();
+
+ @JsonSubtypeCasting
+ void asNotObject();
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/AnyObjectBased.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,16 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+import org.json.simple.JSONArray;
+
+/**
+ * Optional base interface for JSON type interface. Underlying object becomes available
+ * to user this way. The JSON type instance may be created from any supported object
+ * (e.g. from {@link JSONArray}), but may take advantage of this liberty only if it has no fields.
+ */
+public interface AnyObjectBased {
+ Object getUnderlyingObject();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/EnumValueCondition.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,23 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+import java.util.Set;
+
+/**
+ * Implementation of {@link JsonValueCondition} for enum-typed values.
+ * User is supposed to subclass it and specify allowed enum constants in constructor.
+ * @param <T> type of value
+ */
+public abstract class EnumValueCondition<T extends Enum<T>> implements JsonValueCondition<T> {
+ private final Set<T> allowedValues;
+ protected EnumValueCondition(Set<T> allowedValues) {
+ this.allowedValues = allowedValues;
+ }
+
+ public boolean conforms(T value) {
+ return allowedValues.contains(value);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonField.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,25 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Describes a method that corresponds to a type field (i.e. a property of JSON object).
+ * Its use is optional, because all methods by default are recognized as field-reading methods.
+ * Should be used to specify JSON property name.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JsonField {
+ /**
+ * Specifies JSON property name, which otherwise is derived from the method name (optional "get"
+ * prefix is truncated with the first letter decapitalization).
+ */
+ String jsonLiteralName();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonNullable.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * For field-reading method specifies its type as {@code nullable}; it means
+ * that JSON structure may have null value for a corresponding property.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JsonNullable {
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonObjectBased.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,17 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+
+/**
+ * Optional base interface for JSON type interface. Underlying JSON object becomes available
+ * to user this way. The JSON type instance may be created from {@link JSONObject} only
+ * (not from {@link JSONArray} or whatever).
+ */
+public interface JsonObjectBased extends AnyObjectBased {
+ JSONObject getUnderlyingObject();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonOptionalField.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,19 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * For field-reading method specifies that the field is optional and may safely be absent in
+ * JSON object. By default fields are not optional.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JsonOptionalField {
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonOverrideField.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,20 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * For field-reading method, specifies that it is overridden. Generally, field should not be
+ * declared both in type and subtype. However this might be needed for declaring field in
+ * base type for general use and in subtype for specifying subtype conditions.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JsonOverrideField {
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonProtocolModelParseException.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,23 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+/**
+ * Signals that JSON model has some problem in it.
+ */
+public class JsonProtocolModelParseException extends Exception {
+ public JsonProtocolModelParseException() {
+ super();
+ }
+ public JsonProtocolModelParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ public JsonProtocolModelParseException(String message) {
+ super(message);
+ }
+ public JsonProtocolModelParseException(Throwable cause) {
+ super(cause);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonProtocolParseException.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,23 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+/**
+ * Signals a failure during JSON object parsing.
+ */
+public class JsonProtocolParseException extends Exception {
+ public JsonProtocolParseException() {
+ super();
+ }
+ public JsonProtocolParseException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ public JsonProtocolParseException(String message) {
+ super(message);
+ }
+ public JsonProtocolParseException(Throwable cause) {
+ super(cause);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonSubtype.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,14 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+/**
+ * A base interface for JSON subtype interface. This inheritance serves 2 purposes:
+ * it declares base type (visible to human and to interface analyzer) and adds {@link #getSuper()}
+ * getter that may be directly used in programs.
+ */
+public interface JsonSubtype<BASE> {
+ BASE getSuper();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonSubtypeCasting.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,25 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks method as method casting to a subtype. Normally the method return type should be
+ * some other json type, which serves as subtype; the subtype interface must extend
+ * {@link JsonSubtype} (with correct generic parameter).
+ * <p>However for types, annotated as <code>{@link JsonType#subtypesChosenManually()} = true</code>,
+ * the method may return something other than json type; it also may return any json type (free of
+ * mandatory {@link JsonSubtype} inheritance), provided that
+ * <code>{@link #reinterpret()} = true</code>.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+public @interface JsonSubtypeCasting {
+ boolean reinterpret() default false;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonSubtypeCondition.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specifies a condition for a field-reading method. It is one from group of annotations
+ * that mark key fields for choosing particular subtype. They set conditions on JSON fields in
+ * a subtype interface that drive subtype auto-selection at parsing time.
+ * <p>
+ * Specifies a general condition; it requires that:
+ * <ul>
+ * <li>field should be null, if {@link #valueIsNull()} is true,
+ * <li>field should be absent, if {@link #fieldIsAbsent()} is true,
+ * <li>field should exist, if no options are set.
+ * </ul>
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JsonSubtypeCondition {
+ boolean fieldIsAbsent() default false;
+ boolean valueIsNull() default false;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonSubtypeConditionBoolValue.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,26 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specifies a condition for a field-reading method. It is one from group of annotations that
+ * mark key fields for choosing particular subtype. They set conditions on JSON fields in a subtype
+ * interface that drive subtype auto-selection at parsing time.
+ * <p>
+ * Specifies condition on a boolean value (for boolean-valued fields).
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JsonSubtypeConditionBoolValue {
+ /**
+ * A value of boolean field that satisfies condition.
+ */
+ boolean value();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonSubtypeConditionCustom.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specifies a condition for a field-reading method. It is one from group of annotations that
+ * mark key fields for choosing particular subtype. They set conditions on JSON fields in a subtype
+ * interface that drive subtype auto-selection at parsing time.
+ * <p>
+ * Specifies a user-provided condition; works for fields of any type.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JsonSubtypeConditionCustom {
+ /**
+ * Specifies user-provided condition in form of class with default constructor, that
+ * implements {@link JsonValueCondition} interface.
+ */
+ Class<? extends JsonValueCondition<?>> condition();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonType.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,45 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+
+/**
+ * Marks an interface as interface to json type object. This way user may hold JSON object in
+ * form of statically typed Java interface. The interface provides methods for reading properties
+ * (here called fields, because we imply there are "types" in JSON) and for accessing subtypes.
+ * <p>
+ * In this design casting to subtypes means getting a different object of the subtype interface.
+ * For a type interface, a set of subtypes is defined by its methods
+ * with {@link JsonSubtypeCasting} annotation. These methods provide access to subtype objects.
+ * From the parsing point of view, subtypes are supported in 2 different ways, as controlled
+ * by {@link #subtypesChosenManually()} flag:
+ * <ul>
+ * <li>{@link #subtypesChosenManually()} is false; when parsing, a particular subtype is selected
+ * automatically from set of all possible subtypes. JsonSubtypeCondition* annotations in subtypes
+ * define conditions for selection. Subtype object (together with sub-subtype object, etc) is
+ * created at the time of parsing. An empty subtype, which is selected if nothing else matches,
+ * may be declared with void-returning {@link JsonSubtypeCasting}-marked method.
+ * <li>{@link #subtypesChosenManually()} is true; subtype is not determined automatically. Instead,
+ * clients may choose a casting method themselves and invoke parsing and object creation at runtime.
+ * JsonType objects with {@link #subtypesChosenManually()}=true may be built not only on
+ * {@link JSONObject}, but also on {@link JSONArray} etc.
+ * </ul>
+ * <p>
+ * To provide access to underlying {@link JSONObject} the type interface may extend
+ * {@link JsonObjectBased} interface. To provide access to underlying object
+ * (not necessarily JSONObject) type interface may extend {@link AnyObjectBased} interface.
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JsonType {
+ boolean subtypesChosenManually() default false;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonValueCondition.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,18 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser;
+
+/**
+ * A condition for property value. Implementation may provide any logic here.
+ * @param <T> type of value
+ * @see JsonSubtypeConditionCustom
+ */
+public interface JsonValueCondition<T> {
+ /**
+ * @param value parsed data from JSON property
+ * @return true if value satisfies condition
+ */
+ boolean conforms(T value);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/BaseHandlersLibrary.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,111 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.chromium.sdk.internal.protocolparser.AnyObjectBased;
+import org.chromium.sdk.internal.protocolparser.JsonObjectBased;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.json.simple.JSONObject;
+
+/**
+ * Contains dynamic proxy method handlers for several well-known methods.
+ */
+class BaseHandlersLibrary {
+ public static BaseHandlersLibrary INSTANCE;
+
+ public Map<Method, ? extends MethodHandler> getAllHandlers() {
+ return method2Handler;
+ }
+
+ private final Map<Method, MethodHandler> method2Handler;
+
+ private BaseHandlersLibrary() throws NoSuchMethodException {
+ method2Handler = new HashMap<Method, MethodHandler>();
+ Method[] objectMethods = {
+ Object.class.getMethod("equals", Object.class),
+ Object.class.getMethod("hashCode"),
+ Object.class.getMethod("toString")
+ };
+ for (Method m : objectMethods) {
+ method2Handler.put(m, new SelfCallMethodHanlder(m));
+ }
+ fill(method2Handler, new GetJsonObjectMethodHaldler(), new GetAnyObjectMethodHaldler(),
+ new GetSuperMethodHaldler());
+ }
+
+ private static void fill(Map<Method, MethodHandler> map, MethodHandlerBase ... handlers) {
+ for (MethodHandlerBase handler : handlers) {
+ map.put(handler.getMethod(), handler);
+ }
+ }
+
+ private static abstract class MethodHandlerBase extends MethodHandler {
+ private final Method method;
+ MethodHandlerBase(Method method) {
+ this.method = method;
+ }
+ Method getMethod() {
+ return method;
+ }
+ }
+
+ private static class SelfCallMethodHanlder extends MethodHandlerBase {
+ SelfCallMethodHanlder(Method method) {
+ super(method);
+ }
+
+ @Override
+ Object handle(Object myself, ObjectData objectData, Object[] args)
+ throws IllegalAccessException, InvocationTargetException {
+ return getMethod().invoke(myself, args);
+ }
+ }
+
+ private static class GetJsonObjectMethodHaldler extends MethodHandlerBase {
+ GetJsonObjectMethodHaldler() throws NoSuchMethodException {
+ super(JsonObjectBased.class.getMethod("getUnderlyingObject"));
+ }
+
+ @Override
+ JSONObject handle(Object myself, ObjectData objectData, Object[] args) {
+ return (JSONObject) objectData.getUnderlyingObject();
+ }
+ }
+
+ private static class GetAnyObjectMethodHaldler extends MethodHandlerBase {
+ GetAnyObjectMethodHaldler() throws NoSuchMethodException {
+ super(AnyObjectBased.class.getMethod("getUnderlyingObject"));
+ }
+
+ @Override
+ Object handle(Object myself, ObjectData objectData, Object[] args) {
+ return objectData.getUnderlyingObject();
+ }
+ }
+
+ private static class GetSuperMethodHaldler extends MethodHandlerBase {
+ GetSuperMethodHaldler() throws NoSuchMethodException {
+ super(JsonSubtype.class.getMethod("getSuper"));
+ }
+
+ @Override
+ Object handle(Object myself, ObjectData objectData, Object[] args) {
+ return objectData.getSuperObjectData().getProxy();
+ }
+ }
+
+ static {
+ try {
+ INSTANCE = new BaseHandlersLibrary();
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/EnumParser.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,59 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.chromium.sdk.internal.protocolparser.JsonProtocolModelParseException;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+
+class EnumParser<T extends Enum<T>> extends QuickParser<T> {
+ public static <T extends Enum<T>> EnumParser<T> create(Class<T> enumTypeClass,
+ boolean isNullable) throws JsonProtocolModelParseException {
+ return new EnumParser<T>(enumTypeClass, isNullable);
+ }
+
+ private final Method methodValueOf;
+ private final boolean isNullable;
+ private final Class<T> enumClass;
+
+ private EnumParser(Class<T> enumClass, boolean isNullable)
+ throws JsonProtocolModelParseException {
+ this.enumClass = enumClass;
+ this.isNullable = isNullable;
+ try {
+ this.methodValueOf = enumClass.getMethod("valueOf", String.class);
+ } catch (NoSuchMethodException e) {
+ throw new JsonProtocolModelParseException(
+ "Failed to find valueOf method for parsing strings", e);
+ }
+ }
+
+ @Override
+ public T parseValueQuick(Object value) throws JsonProtocolParseException {
+ if (isNullable && value == null) {
+ return null;
+ }
+ if (value instanceof String == false) {
+ throw new JsonProtocolParseException("String value expected");
+ }
+ String stringValue = (String) value;
+ T result;
+ try {
+ result = enumClass.cast(methodValueOf.invoke(null, stringValue));
+ } catch (IllegalArgumentException e) {
+ throw new JsonProtocolParseException("Failed to parse enum constant " + stringValue, e);
+ } catch (IllegalAccessException e) {
+ throw new JsonProtocolParseException("Failed to call valueOf method", e);
+ } catch (InvocationTargetException e) {
+ throw new JsonProtocolParseException("Failed to call valueOf method", e);
+ }
+ if (result == null) {
+ throw new JsonProtocolParseException("Failed to parse value " + value + " as enum");
+ }
+ return result;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldCondition.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,41 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+import org.chromium.sdk.internal.protocolparser.JsonProtocolModelParseException;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+
+/**
+ * An implementation of JsonSubtypeCondition* annotations. Basically it only holds all parameters
+ * and delegates actual condition evaluating to {@link #conditionLogic}.
+ */
+class FieldCondition {
+ private final String propertyName;
+ private final QuickParser<?> quickParser;
+ private final FieldConditionLogic conditionLogic;
+
+ FieldCondition(String propertyName, QuickParser<?> quickParser,
+ FieldConditionLogic conditionLogic) throws JsonProtocolModelParseException {
+ if (conditionLogic.requiresQuickParser() && quickParser == null) {
+ throw new JsonProtocolModelParseException(
+ "The choose condition does not work with the type of " + propertyName);
+ }
+ this.propertyName = propertyName;
+ this.quickParser = quickParser;
+ this.conditionLogic = conditionLogic;
+ }
+
+ String getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * @param hasValue whether field exists in JSON object (however its value may be null)
+ * @param unparsedValue value of the field if hasValue is true or undefined otherwise
+ */
+ boolean checkValue(boolean hasValue, Object unparsedValue) throws JsonProtocolParseException {
+ return conditionLogic.checkValue(hasValue, unparsedValue, quickParser);
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,129 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.chromium.sdk.internal.protocolparser.JsonProtocolModelParseException;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCondition;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeConditionBoolValue;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeConditionCustom;
+import org.chromium.sdk.internal.protocolparser.JsonValueCondition;
+
+/**
+ * An interface to field conditions logic. Some conditions are simple and never need parsed
+ * values, others are more fine-grained and require quick parser before making actual checks.
+ */
+abstract class FieldConditionLogic {
+ private final boolean logicRequiresQuickParser;
+
+ FieldConditionLogic(boolean logicRequiresQuickParser) {
+ this.logicRequiresQuickParser = logicRequiresQuickParser;
+ }
+
+ boolean requiresQuickParser() {
+ return logicRequiresQuickParser;
+ }
+
+ /**
+ * @param hasValue whether field exists in JSON object (however its value may be null)
+ * @param quickParser parser that may be used if {@link #requiresQuickParser()} is true
+ */
+ abstract boolean checkValue(boolean hasValue, Object unparsedValue, QuickParser<?> quickParser)
+ throws JsonProtocolParseException;
+
+ /**
+ * Constructor function that creates field condition logic from method annotations.
+ */
+ static FieldConditionLogic readLogic(Method m) throws JsonProtocolModelParseException {
+ List<FieldConditionLogic> results = new ArrayList<FieldConditionLogic>(1);
+ JsonSubtypeConditionBoolValue boolValueAnn =
+ m.getAnnotation(JsonSubtypeConditionBoolValue.class);
+ if (boolValueAnn != null) {
+ final Boolean required = boolValueAnn.value();
+ results.add(new FieldConditionLogic(true) {
+ @Override
+ boolean checkValue(boolean hasValue, Object unparsedValue, QuickParser<?> parser)
+ throws JsonProtocolParseException {
+ return hasValue && required == parser.parseValueQuick(unparsedValue);
+ }
+ });
+ }
+ JsonSubtypeConditionCustom customAnn = m.getAnnotation(JsonSubtypeConditionCustom.class);
+ if (customAnn != null) {
+ final CustomConditionWrapper<?> constraint =
+ CustomConditionWrapper.create(customAnn.condition());
+ results.add(new FieldConditionLogic(true) {
+ @Override
+ boolean checkValue(boolean hasValue, Object unparsedValue, QuickParser<?> parser)
+ throws JsonProtocolParseException {
+ return hasValue && constraint.checkValue(parser.parseValueQuick(unparsedValue));
+ }
+ });
+ }
+ JsonSubtypeCondition conditionAnn = m.getAnnotation(JsonSubtypeCondition.class);
+ if (conditionAnn != null) {
+ int savedResSize = results.size();
+ if (conditionAnn.fieldIsAbsent()) {
+ results.add(new FieldConditionLogic(false) {
+ @Override
+ boolean checkValue(boolean hasValue, Object unparsedValue, QuickParser<?> parser) {
+ return !hasValue;
+ }
+ });
+ }
+ if (conditionAnn.valueIsNull()) {
+ results.add(new FieldConditionLogic(false) {
+ @Override
+ boolean checkValue(boolean hasValue, Object unparsedValue, QuickParser<?> parser) {
+ return hasValue && unparsedValue != null;
+ }
+ });
+ }
+ if (savedResSize == results.size()) {
+ results.add(new FieldConditionLogic(false) {
+ @Override
+ boolean checkValue(boolean hasValue, Object unparsedValue, QuickParser<?> parser) {
+ return hasValue;
+ }
+ });
+ }
+ }
+ if (results.size() == 0) {
+ return null;
+ }
+ if (results.size() > 1) {
+ throw new JsonProtocolModelParseException("Too many constraints for field getter " + m);
+ }
+ return results.get(0);
+ }
+
+ private static class CustomConditionWrapper<T> {
+ static <T, CL extends JsonValueCondition<T>> CustomConditionWrapper<T> create(
+ Class<CL> constraintClass) {
+ return new CustomConditionWrapper<T>(constraintClass);
+ }
+
+ private final JsonValueCondition<? super T> constraint;
+
+ private CustomConditionWrapper(Class<? extends JsonValueCondition<? super T>> constraintClass) {
+ try {
+ constraint = constraintClass.newInstance();
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ boolean checkValue(Object parsedValue) {
+ return constraint.conforms((T)parsedValue);
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldLoadedFinisher.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,14 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+/**
+ * Defines object responsible for converting values saved in {@link ObjectData} to types
+ * returned to user. It is necessary, because for json type fields we save {@link ObjectData}
+ * rather than instance of the type itself.
+ */
+abstract class FieldLoadedFinisher {
+ abstract Object getValueForUser(Object cachedValue);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldLoader.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,44 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+
+/**
+ * This classs is responsible for parsing field values and saving them in {@link ObjectData}
+ * for future use.
+ */
+class FieldLoader {
+ private final String fieldName;
+ private final int fieldPosInArray;
+ private final SlowParser<?> slowParser;
+ private final boolean isOptional;
+
+ FieldLoader(int fieldPosInArray, String fieldName, SlowParser<?> slowParser, boolean isOptional) {
+ this.fieldName = fieldName;
+ this.fieldPosInArray = fieldPosInArray;
+ this.slowParser = slowParser;
+ this.isOptional = isOptional;
+ }
+
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ public void parse(boolean hasValue, Object value, ObjectData objectData)
+ throws JsonProtocolParseException {
+ if (hasValue) {
+ try {
+ objectData.getFieldArray()[fieldPosInArray] = slowParser.parseValue(value, objectData);
+ } catch (JsonProtocolParseException e) {
+ throw new JsonProtocolParseException("Failed to parse field " + getFieldName(), e);
+ }
+ } else {
+ if (!isOptional) {
+ throw new JsonProtocolParseException("Field is not optional: " + getFieldName());
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonInvocationHandler.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,35 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+/**
+ * The implementation of {@link InvocationHandler} for JSON types. It dispatches calls to method
+ * handlers from the map.
+ */
+class JsonInvocationHandler implements InvocationHandler {
+ private final ObjectData objectData;
+ private final Map<Method, MethodHandler> methodHandlerMap;
+
+ JsonInvocationHandler(ObjectData objectData, Map<Method, MethodHandler> methodHandlerMap) {
+ this.objectData = objectData;
+ this.methodHandlerMap = methodHandlerMap;
+ }
+
+ ObjectData getObjectData() {
+ return objectData;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ MethodHandler methodHandler = methodHandlerMap.get(method);
+ if (methodHandler == null) {
+ throw new RuntimeException("No method handler for " + method);
+ }
+ return methodHandler.handle(proxy, objectData, args);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,870 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.chromium.sdk.internal.protocolparser.JsonField;
+import org.chromium.sdk.internal.protocolparser.JsonNullable;
+import org.chromium.sdk.internal.protocolparser.JsonOptionalField;
+import org.chromium.sdk.internal.protocolparser.JsonOverrideField;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolModelParseException;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.protocolparser.JsonSubtype;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCasting;
+import org.chromium.sdk.internal.protocolparser.JsonType;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+
+/**
+ * Java dynamic-proxy based parser for set of json types. It uses set of type interfaces
+ * as model description and provides implementations for them. JsonProtocolParser
+ * converts JSONObject into a required Java type instance.
+ */
+public class JsonProtocolParser {
+ private final Map<Class<?>, TypeHandler<?>> type2TypeHandler;
+
+ /**
+ * Constructs parser from a set of type interfaces.
+ */
+ public JsonProtocolParser(Class<?> ... protocolInterfaces)
+ throws JsonProtocolModelParseException {
+ this(Arrays.asList(protocolInterfaces), Collections.<JsonProtocolParser>emptyList());
+ }
+
+ /**
+ * Constructs parser from a set of type interfaces and a list of base packages. Type interfaces
+ * may reference to type interfaces from base packages.
+ * @param basePackages list of base packages in form of list of {@link JsonProtocolParser}'s
+ */
+ public JsonProtocolParser(List<? extends Class<?>> protocolInterfaces,
+ List<? extends JsonProtocolParser> basePackages) throws JsonProtocolModelParseException {
+ type2TypeHandler = readTypes(protocolInterfaces, basePackages);
+ }
+
+ /**
+ * Parses {@link JSONObject} as typeClass type.
+ */
+ public <T> T parse(JSONObject object, Class<T> typeClass) throws JsonProtocolParseException {
+ return parseAnything(object, typeClass);
+ }
+
+ /**
+ * Parses any object as typeClass type. Non-JSONObject only makes sense for
+ * types with {@link JsonType#subtypesChosenManually()} = true annotation.
+ */
+ public <T> T parseAnything(Object object, Class<T> typeClass) throws JsonProtocolParseException {
+ TypeHandler<T> type = type2TypeHandler.get(typeClass).cast(typeClass);
+ return type.parseRoot(object);
+ }
+
+ private static Map<Class<?>, TypeHandler<?>> readTypes(
+ List<? extends Class<?>> protocolInterfaces,
+ final List<? extends JsonProtocolParser> basePackages)
+ throws JsonProtocolModelParseException {
+ ReadInterfacesSession session = new ReadInterfacesSession(protocolInterfaces, basePackages);
+ session.go();
+ return session.getResult();
+ }
+
+
+ private static class ReadInterfacesSession {
+ private final Map<Class<?>, TypeHandler<?>> type2typeHandler;
+ private final List<? extends JsonProtocolParser> basePackages;
+
+ final List<RefImpl<?>> refs = new ArrayList<RefImpl<?>>();
+ final List<SubtypeCaster> subtypeCasters =
+ new ArrayList<SubtypeCaster>();
+
+ ReadInterfacesSession(List<? extends Class<?>> protocolInterfaces,
+ List<? extends JsonProtocolParser> basePackages) {
+ this.type2typeHandler = new HashMap<Class<?>, TypeHandler<?>>();
+ this.basePackages = basePackages;
+
+ for (Class<?> typeClass : protocolInterfaces) {
+ type2typeHandler.put(typeClass, null);
+ }
+ }
+
+ void go() throws JsonProtocolModelParseException {
+ // Create TypeHandler's.
+ for (Class<?> typeClass : type2typeHandler.keySet()) {
+ TypeHandler<?> typeHandler = createTypeHandler(typeClass);
+ type2typeHandler.put(typeClass, typeHandler);
+ }
+
+ // Resolve cross-references.
+ for (RefImpl<?> ref : refs) {
+ TypeHandler<?> type = type2typeHandler.get(ref.typeClass);
+ if (type == null) {
+ throw new RuntimeException();
+ }
+ ref.set(type);
+ }
+
+ // Set subtype casters.
+ for (SubtypeCaster subtypeCaster : subtypeCasters) {
+ TypeHandler<?> subtypeHandler = subtypeCaster.getSubtypeHandler();
+ subtypeHandler.getSubtypeSupport().setSubtypeCaster(subtypeCaster);
+ }
+
+ // Check subtype casters consistency.
+ for (TypeHandler<?> type : type2typeHandler.values()) {
+ type.getSubtypeSupport().checkHasSubtypeCaster();
+ }
+ }
+
+ Map<Class<?>, TypeHandler<?>> getResult() {
+ return type2typeHandler;
+ }
+
+ private <T> TypeHandler<T> createTypeHandler(Class<T> typeClass)
+ throws JsonProtocolModelParseException {
+ if (!typeClass.isInterface()) {
+ throw new JsonProtocolModelParseException("Json model type should be interface: " +
+ typeClass.getName());
+ }
+
+ FieldProcessor<T> fields = new FieldProcessor<T>(typeClass);
+
+ fields.go();
+
+ Map<Method, MethodHandler> methodHandlerMap = fields.getMethodHandlerMap();
+ methodHandlerMap.putAll(BaseHandlersLibrary.INSTANCE.getAllHandlers());
+
+ TypeHandler.EagerFieldParser eagerFieldParser =
+ new EagerFieldParserImpl(fields.getOnDemandHanlers());
+
+ RefToType<?> superclassRef = getSuperclassRef(typeClass);
+
+ return new TypeHandler<T>(typeClass, superclassRef,
+ fields.getFieldArraySize(), methodHandlerMap, fields.getFieldLoaders(),
+ fields.getFieldConditions(), eagerFieldParser, fields.getAlgCasesData());
+ }
+
+ private SlowParser<?> getFieldTypeParser(Type type, boolean declaredNullable,
+ boolean isSubtyping) throws JsonProtocolModelParseException {
+ if (type instanceof Class) {
+ Class<?> typeClass = (Class<?>) type;
+ if (type == Long.class) {
+ nullableIsNotSupported(declaredNullable);
+ return LONG_PARSER.getNullable();
+ } else if (type == Long.TYPE) {
+ nullableIsNotSupported(declaredNullable);
+ return LONG_PARSER.getNotNullable();
+ } else if (type == Boolean.class) {
+ nullableIsNotSupported(declaredNullable);
+ return BOOLEAN_PARSER.getNullable();
+ } else if (type == Boolean.TYPE) {
+ nullableIsNotSupported(declaredNullable);
+ return BOOLEAN_PARSER.getNotNullable();
+ } else if (type == Void.class) {
+ nullableIsNotSupported(declaredNullable);
+ return VOID_PARSER;
+ } else if (type == String.class) {
+ return STRING_PARSER.get(declaredNullable);
+ } else if (type == Object.class) {
+ return OBJECT_PARSER.get(declaredNullable);
+ } else if (type == JSONObject.class) {
+ return JSON_PARSER.get(declaredNullable);
+ } else if (typeClass.isEnum()) {
+ Class<RetentionPolicy> enumTypeClass = (Class<RetentionPolicy>) typeClass;
+ return EnumParser.create(enumTypeClass, declaredNullable);
+ } else if (type2typeHandler.containsKey(typeClass)) {
+ }
+ RefToType<?> ref = getTypeRef(typeClass);
+ if (ref != null) {
+ return createJsonParser(ref, declaredNullable, isSubtyping);
+ }
+ throw new JsonProtocolModelParseException("Method return type " + type +
+ " (simple class) not supported");
+ } else if (type instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) type;
+ if (parameterizedType.getRawType() == List.class) {
+ Type argumentType = parameterizedType.getActualTypeArguments()[0];
+ if (argumentType instanceof WildcardType) {
+ WildcardType wildcard = (WildcardType) argumentType;
+ if (wildcard.getLowerBounds().length == 0 && wildcard.getUpperBounds().length == 1) {
+ argumentType = wildcard.getUpperBounds()[0];
+ }
+ }
+ SlowParser<?> componentParser = getFieldTypeParser(argumentType, false, false);
+ return createArrayParser(componentParser, declaredNullable);
+ } else {
+ throw new JsonProtocolModelParseException("Method return type " + type +
+ " (generic) not supported");
+ }
+ } else {
+ throw new JsonProtocolModelParseException("Method return type " + type + " not supported");
+ }
+ }
+
+ private void nullableIsNotSupported(boolean declaredNullable)
+ throws JsonProtocolModelParseException {
+ if (declaredNullable) {
+ throw new JsonProtocolModelParseException("The type cannot be declared nullable");
+ }
+ }
+
+ private <T> JsonTypeParser<T> createJsonParser(RefToType<T> type, boolean isNullable,
+ boolean isSubtyping) {
+ return new JsonTypeParser<T>(type, isNullable, isSubtyping);
+ }
+
+ private <T> ArrayParser<T> createArrayParser(SlowParser<T> componentParser,
+ boolean isNullable) {
+ return new ArrayParser<T>(componentParser, isNullable);
+ }
+
+ private <T> RefToType<T> getTypeRef(final Class<T> typeClass) {
+ if (type2typeHandler.containsKey(typeClass)) {
+ RefImpl<T> result = new RefImpl<T>(typeClass);
+ refs.add(result);
+ return result;
+ }
+ for (JsonProtocolParser baseParser : basePackages) {
+ TypeHandler<?> typeHandler = baseParser.type2TypeHandler.get(typeClass);
+ if (typeHandler != null) {
+ final TypeHandler<T> typeHandlerT = (TypeHandler<T>) typeHandler;
+ return new RefToType<T>() {
+ @Override
+ TypeHandler<T> get() {
+ return typeHandlerT;
+ }
+ @Override
+ Class<?> getTypeClass() {
+ return typeClass;
+ }
+ };
+ }
+ }
+ return null;
+ }
+
+ private RefToType<?> getSuperclassRef(Class<?> typeClass)
+ throws JsonProtocolModelParseException {
+ RefToType<?> result = null;
+ for (Type interfc : typeClass.getGenericInterfaces()) {
+ if (interfc instanceof ParameterizedType == false) {
+ continue;
+ }
+ ParameterizedType parameterizedType = (ParameterizedType) interfc;
+ if (parameterizedType.getRawType() != JsonSubtype.class) {
+ continue;
+ }
+ Type param = parameterizedType.getActualTypeArguments()[0];
+ if (param instanceof Class == false) {
+ throw new JsonProtocolModelParseException("Unexpected type of superclass " + param);
+ }
+ Class<?> paramClass = (Class<?>) param;
+ if (result != null) {
+ throw new JsonProtocolModelParseException("Already has superclass " +
+ result.getTypeClass().getName());
+ }
+ result = getTypeRef(paramClass);
+ if (result == null) {
+ throw new JsonProtocolModelParseException("Unknown base class " + paramClass.getName());
+ }
+ }
+ return result;
+ }
+
+ class FieldProcessor<T> {
+ private final Class<T> typeClass;
+
+ private final JsonType jsonTypeAnn;
+ private final List<FieldLoader> fieldLoaders = new ArrayList<FieldLoader>(2);
+ private final List<LazyParseFieldMethodHandler> onDemandHanlers =
+ new ArrayList<LazyParseFieldMethodHandler>();
+ private final Map<Method, MethodHandler> methodHandlerMap =
+ new HashMap<Method, MethodHandler>();
+ private final FieldMap fieldMap = new FieldMap();
+ private final List<FieldCondition> fieldConditions = new ArrayList<FieldCondition>(2);
+ private AlgebraicCasesDataImpl algCasesData = null;
+ private int fieldArraySize = 0;
+
+ FieldProcessor(Class<T> typeClass) throws JsonProtocolModelParseException {
+ this.typeClass = typeClass;
+ jsonTypeAnn = typeClass.getAnnotation(JsonType.class);
+ if (jsonTypeAnn == null) {
+ throw new JsonProtocolModelParseException("Not a json model type: " + typeClass);
+ }
+ }
+
+ void go() throws JsonProtocolModelParseException {
+ for (Method m : typeClass.getDeclaredMethods()) {
+ try {
+ processMethod(m);
+ } catch (JsonProtocolModelParseException e) {
+ throw new JsonProtocolModelParseException("Problem with method " + m, e);
+ }
+ }
+ }
+
+ private void processMethod(Method m) throws JsonProtocolModelParseException {
+ if (m.getParameterTypes().length != 0) {
+ throw new JsonProtocolModelParseException("No parameters expected in " + m);
+ }
+ JsonOverrideField overrideFieldAnn = m.getAnnotation(JsonOverrideField.class);
+ FieldConditionLogic fieldConditionLogic = FieldConditionLogic.readLogic(m);
+ String fieldName = checkAndGetJsonFieldName(m);
+ MethodHandler methodHandler;
+
+ JsonSubtypeCasting jsonSubtypeCaseAnn = m.getAnnotation(JsonSubtypeCasting.class);
+ if (jsonSubtypeCaseAnn != null) {
+ if (fieldConditionLogic != null) {
+ throw new JsonProtocolModelParseException(
+ "Subtype condition annotation only works with field getter methods");
+ }
+ if (overrideFieldAnn != null) {
+ throw new JsonProtocolModelParseException(
+ "Override annotation only works with field getter methods");
+ }
+ if (algCasesData == null) {
+ algCasesData = new AlgebraicCasesDataImpl(jsonTypeAnn.subtypesChosenManually());
+ }
+
+ if (jsonTypeAnn.subtypesChosenManually()) {
+ methodHandler = processManualSubtypeMethod(m, jsonSubtypeCaseAnn);
+ } else {
+ if (jsonSubtypeCaseAnn.reinterpret()) {
+ throw new JsonProtocolModelParseException(
+ "Option 'reinterpret' is only available with 'subtypes chosen manually'");
+ }
+ methodHandler = processAutomaticSubtypeMethod(m);
+ }
+
+ } else {
+ methodHandler = processFieldGetterMethod(m, fieldConditionLogic, overrideFieldAnn,
+ fieldName);
+ }
+ methodHandlerMap.put(m, methodHandler);
+ }
+
+ private MethodHandler processFieldGetterMethod(Method m,
+ FieldConditionLogic fieldConditionLogic, JsonOverrideField overrideFieldAnn,
+ String fieldName) throws JsonProtocolModelParseException {
+ MethodHandler methodHandler;
+ JsonNullable nullableAnn = m.getAnnotation(JsonNullable.class);
+ SlowParser<?> fieldTypeParser = getFieldTypeParser(m.getGenericReturnType(),
+ nullableAnn != null, false);
+ if (fieldConditionLogic != null) {
+ fieldConditions.add(new FieldCondition(fieldName, fieldTypeParser.asQuickParser(),
+ fieldConditionLogic));
+ }
+ if (overrideFieldAnn == null) {
+ fieldMap.localNames.add(fieldName);
+ } else {
+ fieldMap.overridenNames.add(fieldName);
+ }
+
+ boolean isOptional = isOptionalField(m);
+
+ if (fieldTypeParser.asQuickParser() != null) {
+ LazyParseFieldMethodHandler onDemandHandler = new LazyParseFieldMethodHandler(
+ fieldTypeParser.asQuickParser(), isOptional, fieldName);
+ onDemandHanlers.add(onDemandHandler);
+ methodHandler = onDemandHandler;
+ } else {
+ int fieldCode = allocateFieldInArray();
+ FieldLoader fieldLoader = new FieldLoader(fieldCode, fieldName, fieldTypeParser,
+ isOptional);
+ fieldLoaders.add(fieldLoader);
+ methodHandler = new PreparsedFieldMethodHandler(fieldCode,
+ fieldTypeParser.getValueFinisher());
+ }
+ return methodHandler;
+ }
+
+ private MethodHandler processAutomaticSubtypeMethod(Method m)
+ throws JsonProtocolModelParseException {
+ MethodHandler methodHandler;
+ if (m.getReturnType() == Void.TYPE) {
+ if (algCasesData.hasDefaultCase) {
+ throw new JsonProtocolModelParseException("Duplicate default case method: " + m);
+ }
+ algCasesData.hasDefaultCase = true;
+ methodHandler = RETURN_NULL_METHOD_HANDLER;
+ } else {
+ Class<?> methodType = m.getReturnType();
+ RefToType<?> ref = getTypeRef(methodType);
+ if (ref == null) {
+ throw new JsonProtocolModelParseException("Unknown return type in " + m);
+ }
+ if (algCasesData.variantCodeFieldPos == -1) {
+ algCasesData.variantCodeFieldPos = allocateFieldInArray();
+ algCasesData.variantValueFieldPos = allocateFieldInArray();
+ }
+ int algCode = algCasesData.subtypes.size();
+ algCasesData.subtypes.add(ref);
+ final AutoSubtypeMethodHandler algMethodHandler = new AutoSubtypeMethodHandler(
+ algCasesData.variantCodeFieldPos, algCasesData.variantValueFieldPos,
+ algCode);
+ methodHandler = algMethodHandler;
+
+ SubtypeCaster subtypeCaster = new SubtypeCaster(typeClass, ref) {
+ @Override
+ ObjectData getSubtypeObjectData(ObjectData objectData) {
+ return algMethodHandler.getFieldObjectData(objectData);
+ }
+ };
+
+ subtypeCasters.add(subtypeCaster);
+ }
+ return methodHandler;
+ }
+
+
+ private MethodHandler processManualSubtypeMethod(Method m,
+ JsonSubtypeCasting jsonSubtypeCaseAnn) throws JsonProtocolModelParseException {
+ int fieldCode = allocateFieldInArray();
+
+ SlowParser<?> fieldTypeParser = getFieldTypeParser(m.getGenericReturnType(), false,
+ !jsonSubtypeCaseAnn.reinterpret());
+
+ if (!Arrays.asList(m.getExceptionTypes()).contains(JsonProtocolParseException.class)) {
+ throw new JsonProtocolModelParseException(
+ "Method should declare JsonProtocolParseException exception: " + m);
+ }
+
+ final ManualSubtypeMethodHandler handler = new ManualSubtypeMethodHandler(fieldCode,
+ fieldTypeParser);
+ JsonTypeParser<?> parserAsJsonTypeParser = fieldTypeParser.asJsonTypeParser();
+ if (parserAsJsonTypeParser != null && parserAsJsonTypeParser.isSubtyping()) {
+ SubtypeCaster subtypeCaster = new SubtypeCaster(typeClass,
+ parserAsJsonTypeParser.getType()) {
+ @Override
+ ObjectData getSubtypeObjectData(ObjectData baseObjectData)
+ throws JsonProtocolParseException {
+ ObjectData objectData = baseObjectData;
+ return handler.getSubtypeData(objectData);
+ }
+ };
+ subtypeCasters.add(subtypeCaster);
+ }
+ return handler;
+ }
+
+ int getFieldArraySize() {
+ return fieldArraySize;
+ }
+
+ void setFieldArraySize(int fieldArraySize) {
+ this.fieldArraySize = fieldArraySize;
+ }
+
+ AlgebraicCasesDataImpl getAlgCasesData() {
+ return algCasesData;
+ }
+
+ void setAlgCasesData(AlgebraicCasesDataImpl algCasesData) {
+ this.algCasesData = algCasesData;
+ }
+
+ List<FieldLoader> getFieldLoaders() {
+ return fieldLoaders;
+ }
+
+ List<LazyParseFieldMethodHandler> getOnDemandHanlers() {
+ return onDemandHanlers;
+ }
+
+ Map<Method, MethodHandler> getMethodHandlerMap() {
+ return methodHandlerMap;
+ }
+
+ List<FieldCondition> getFieldConditions() {
+ return fieldConditions;
+ }
+
+ private int allocateFieldInArray() {
+ return fieldArraySize++;
+ }
+
+ private boolean isOptionalField(Method m) {
+ JsonOptionalField jsonOptionalFieldAnn = m.getAnnotation(JsonOptionalField.class);
+ return jsonOptionalFieldAnn != null;
+ }
+
+ private String checkAndGetJsonFieldName(Method m) throws JsonProtocolModelParseException {
+ if (m.getParameterTypes().length != 0) {
+ throw new JsonProtocolModelParseException("Must have 0 parameters");
+ }
+ JsonField fieldAnn = m.getAnnotation(JsonField.class);
+ if (fieldAnn != null) {
+ return fieldAnn.jsonLiteralName();
+ }
+ String name = m.getName();
+ if (name.startsWith("get") && name.length() > 3) {
+ name = Character.toLowerCase(name.charAt(3)) + name.substring(4);
+ }
+ return name;
+ }
+ }
+ }
+
+ private static class EagerFieldParserImpl extends TypeHandler.EagerFieldParser {
+ private final List<LazyParseFieldMethodHandler> onDemandHandlers;
+
+ private EagerFieldParserImpl(List<LazyParseFieldMethodHandler> onDemandHandlers) {
+ this.onDemandHandlers = onDemandHandlers;
+ }
+
+ @Override
+ void parseAllFields(ObjectData objectData) throws JsonProtocolParseException {
+ for (LazyParseFieldMethodHandler handler : onDemandHandlers) {
+ handler.parse(objectData);
+ }
+ }
+ }
+
+ private static class LazyParseFieldMethodHandler extends MethodHandler {
+ private final QuickParser<?> quickParser;
+ private final boolean isOptional;
+ private final String fieldName;
+
+ LazyParseFieldMethodHandler(QuickParser<?> quickParser, boolean isOptional, String fieldName) {
+ this.quickParser = quickParser;
+ this.isOptional = isOptional;
+ this.fieldName = fieldName;
+ }
+
+ @Override
+ Object handle(Object myself, ObjectData objectData, Object[] args) {
+ try {
+ return parse(objectData);
+ } catch (JsonProtocolParseException e) {
+ throw new JsonProtocolParseRuntimeException("On demand parsing failed", e);
+ }
+ }
+
+ public Object parse(ObjectData objectData) throws JsonProtocolParseException {
+ Map<?,?> properties = (JSONObject)objectData.getUnderlyingObject();
+ Object value = properties.get(fieldName);
+ boolean hasValue;
+ if (value == null) {
+ hasValue = properties.containsKey(fieldName);
+ } else {
+ hasValue = true;
+ }
+ return parse(hasValue, value, objectData);
+ }
+
+ public Object parse(boolean hasValue, Object value, ObjectData objectData)
+ throws JsonProtocolParseException {
+ if (hasValue) {
+ try {
+ return quickParser.parseValueQuick(value);
+ } catch (JsonProtocolParseException e) {
+ throw new JsonProtocolParseException("Failed to parse field " + fieldName, e);
+ }
+ } else {
+ if (!isOptional) {
+ throw new JsonProtocolParseException("Field is not optional: " + fieldName);
+ }
+ return null;
+ }
+ }
+ }
+
+ private static class PreparsedFieldMethodHandler extends MethodHandler {
+ private final int pos;
+ private final FieldLoadedFinisher valueFinisher;
+
+ PreparsedFieldMethodHandler(int pos, FieldLoadedFinisher valueFinisher) {
+ this.pos = pos;
+ this.valueFinisher = valueFinisher;
+ }
+
+ @Override
+ Object handle(Object myself, ObjectData objectData, Object[] args) throws Throwable {
+ Object val = objectData.getFieldArray()[pos];
+ if (valueFinisher != null) {
+ val = valueFinisher.getValueForUser(val);
+ }
+ return val;
+ }
+ }
+
+ static SlowParser<Void> VOID_PARSER = new QuickParser<Void>() {
+ @Override
+ public Void parseValueQuick(Object value) {
+ return null;
+ }
+ };
+
+ static class SimpleCastParser<T> extends QuickParser<T> {
+ private final boolean nullable;
+ private final Class<T> fieldType;
+
+ SimpleCastParser(Class<T> fieldType, boolean nullable) {
+ this.fieldType = fieldType;
+ this.nullable = nullable;
+ }
+
+ @Override
+ public T parseValueQuick(Object value) throws JsonProtocolParseException {
+ if (value == null) {
+ if (nullable) {
+ return null;
+ } else {
+ throw new JsonProtocolParseException("Field must have type " + fieldType.getName());
+ }
+ }
+ try {
+ return fieldType.cast(value);
+ } catch (ClassCastException e) {
+ throw new JsonProtocolParseException("Field must have type " + fieldType.getName(), e);
+ }
+ }
+
+ @Override
+ public FieldLoadedFinisher getValueFinisher() {
+ return null;
+ }
+ }
+ static class SimpleParserPair<T> {
+ static <T> SimpleParserPair<T> create(Class<T> fieldType) {
+ return new SimpleParserPair<T>(fieldType);
+ }
+
+ private final SimpleCastParser<T> nullable;
+ private final SimpleCastParser<T> notNullable;
+
+ private SimpleParserPair(Class<T> fieldType) {
+ nullable = new SimpleCastParser<T>(fieldType, true);
+ notNullable = new SimpleCastParser<T>(fieldType, false);
+ }
+
+ SimpleCastParser<T> getNullable() {
+ return nullable;
+ }
+
+ SimpleCastParser<T> getNotNullable() {
+ return notNullable;
+ }
+
+ SlowParser<?> get(boolean declaredNullable) {
+ return declaredNullable ? nullable : notNullable;
+ }
+ }
+
+ private static SimpleParserPair<Long> LONG_PARSER = SimpleParserPair.create(Long.class);
+ private static SimpleParserPair<Boolean> BOOLEAN_PARSER = SimpleParserPair.create(Boolean.class);
+ private static SimpleParserPair<String> STRING_PARSER = SimpleParserPair.create(String.class);
+ private static SimpleParserPair<Object> OBJECT_PARSER = SimpleParserPair.create(Object.class);
+ private static SimpleParserPair<JSONObject> JSON_PARSER =
+ SimpleParserPair.create(JSONObject.class);
+
+ static class ArrayParser<T> extends SlowParser<List<? extends T>> {
+ private final SlowParser<T> componentParser;
+ private final boolean isNullable;
+
+ ArrayParser(SlowParser<T> componentParser, boolean isNullable) {
+ this.componentParser = componentParser;
+ this.isNullable = isNullable;
+ }
+
+ @Override
+ public List<? extends T> parseValue(Object value, ObjectData thisData)
+ throws JsonProtocolParseException {
+ if (isNullable && value == null) {
+ return null;
+ }
+ if (value instanceof JSONArray == false) {
+ throw new JsonProtocolParseException("Array value expected");
+ }
+ JSONArray arrayValue = (JSONArray) value;
+ int size = arrayValue.size();
+ List list = new ArrayList<Object>(size);
+ FieldLoadedFinisher valueFinisher = componentParser.getValueFinisher();
+ for (int i = 0; i < size; i++) {
+ // We do not support super object for array component.
+ Object val = componentParser.parseValue(arrayValue.get(i), null);
+ if (valueFinisher != null) {
+ val = valueFinisher.getValueForUser(val);
+ }
+ list.add(val);
+ }
+ return Collections.unmodifiableList(list);
+ }
+ @Override
+ public FieldLoadedFinisher getValueFinisher() {
+ return null;
+ }
+ @Override
+ public JsonTypeParser<?> asJsonTypeParser() {
+ return null;
+ }
+ }
+
+ static MethodHandler RETURN_NULL_METHOD_HANDLER = new MethodHandler() {
+ @Override
+ Object handle(Object myself, ObjectData objectData, Object[] args) throws Throwable {
+ return null;
+ }
+ };
+
+ static class AutoSubtypeMethodHandler extends MethodHandler {
+ private final int variantCodeField;
+ private final int variantValueField;
+ private final int code;
+
+ AutoSubtypeMethodHandler(int variantCodeField, int variantValueField, int code) {
+ this.variantCodeField = variantCodeField;
+ this.variantValueField = variantValueField;
+ this.code = code;
+ }
+
+ ObjectData getFieldObjectData(ObjectData objectData) {
+ Object[] array = objectData.getFieldArray();
+ Integer actualCode = (Integer) array[variantCodeField];
+ if (this.code == actualCode) {
+ ObjectData data = (ObjectData) array[variantValueField];
+ return data;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ Object handle(Object myself, ObjectData objectData, Object[] args) {
+ ObjectData resData = getFieldObjectData(objectData);
+ if (resData == null) {
+ return null;
+ } else {
+ return resData.getProxy();
+ }
+ }
+ }
+
+ static class ManualSubtypeMethodHandler extends MethodHandler {
+ private final int fieldPos;
+ private final SlowParser<?> parser;
+
+ ManualSubtypeMethodHandler(int fieldPos, SlowParser<?> slowParser) {
+ this.fieldPos = fieldPos;
+ this.parser = slowParser;
+ }
+
+ @Override
+ Object handle(Object myself, ObjectData objectData, Object[] args)
+ throws JsonProtocolParseException {
+ return handle(objectData);
+ }
+
+ private Object handleRaw(ObjectData objectData) throws JsonProtocolParseException {
+ Object cachedValue = objectData.getFieldArray()[fieldPos];
+ if (cachedValue == null) {
+ cachedValue = parser.parseValue(objectData.getUnderlyingObject(), objectData);
+ objectData.getFieldArray()[fieldPos] = cachedValue;
+ }
+ return cachedValue;
+ }
+
+ Object handle(ObjectData objectData) throws JsonProtocolParseException {
+ Object res = handleRaw(objectData);
+ FieldLoadedFinisher valueFinisher = parser.getValueFinisher();
+ if (valueFinisher != null) {
+ res = valueFinisher.getValueForUser(res);
+ }
+ return res;
+ }
+
+ ObjectData getSubtypeData(ObjectData objectData) throws JsonProtocolParseException {
+ return (ObjectData) handleRaw(objectData);
+ }
+ }
+
+ static class AlgebraicCasesDataImpl extends TypeHandler.AlgebraicCasesData {
+ private int variantCodeFieldPos = -1;
+ private int variantValueFieldPos = -1;
+ private boolean hasDefaultCase = false;
+ private final List<RefToType<?>> subtypes = new ArrayList<RefToType<?>>();
+ private final boolean isManualChoose;
+
+ AlgebraicCasesDataImpl(boolean isManualChoose) {
+ this.isManualChoose = isManualChoose;
+ }
+
+ @Override
+ int getVariantCodeFieldPos() {
+ return variantCodeFieldPos;
+ }
+
+ @Override
+ int getVariantValueFieldPos() {
+ return variantValueFieldPos;
+ }
+
+ @Override
+ boolean hasDefaultCase() {
+ return hasDefaultCase;
+ }
+
+ @Override
+ List<RefToType<?>> getSubtypes() {
+ return subtypes;
+ }
+
+ @Override
+ boolean isManualChoose() {
+ return isManualChoose;
+ }
+ }
+
+ private static class RefImpl<T> extends RefToType<T> {
+ private final Class<T> typeClass;
+ private TypeHandler<T> type = null;
+
+ RefImpl(Class<T> typeClass) {
+ this.typeClass = typeClass;
+ }
+
+ @Override
+ Class<?> getTypeClass() {
+ return typeClass;
+ }
+
+ @Override
+ TypeHandler<T> get() {
+ return type;
+ }
+
+ void set(TypeHandler<?> type) {
+ this.type = (TypeHandler<T>)type;
+ }
+ }
+
+ // We should use it for static analysis later.
+ private static class FieldMap {
+ final List<String> localNames = new ArrayList<String>(5);
+ final List<String> overridenNames = new ArrayList<String>(1);
+ }
+
+ private static class JsonProtocolParseRuntimeException extends RuntimeException {
+ JsonProtocolParseRuntimeException() {
+ }
+ JsonProtocolParseRuntimeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ JsonProtocolParseRuntimeException(String message) {
+ super(message);
+ }
+ JsonProtocolParseRuntimeException(Throwable cause) {
+ super(cause);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonTypeParser.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,75 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.json.simple.JSONObject;
+
+/**
+ * A parser that generates dynamic proxy implementation of JsonType interface
+ * for a {@link JSONObject}.
+ * It creates dynamic proxy instance in 2 steps. First {@link #parseValue(Object, ObjectData)}
+ * outputs {@link ObjectData}, which gets stored in field storage array. Later, when we are
+ * about to return the value to a user, it is converted to a dynamic proxy instance by
+ * {@link #VALUE_FINISHER} converter. We have to store an intermediate value for easier data
+ * manipulation (dynamic proxy does not have any interfaces that we could make use of).
+ */
+class JsonTypeParser<T> extends SlowParser<ObjectData> {
+ private final RefToType<T> refToType;
+ private final boolean isNullable;
+ private final boolean isSubtyping;
+
+ JsonTypeParser(RefToType<T> refToType, boolean isNullable, boolean isSubtyping) {
+ this.refToType = refToType;
+ this.isNullable = isNullable;
+ this.isSubtyping = isSubtyping;
+ }
+
+ RefToType<T> getType() {
+ return refToType;
+ }
+
+ @Override
+ public ObjectData parseValue(Object value, ObjectData thisData)
+ throws JsonProtocolParseException {
+ if (isNullable && value == null) {
+ return null;
+ }
+ if (value == null) {
+ throw new JsonProtocolParseException("null input");
+ }
+ TypeHandler<T> typeHandler = refToType.get();
+ if (isSubtyping) {
+ return typeHandler.parse(value, thisData);
+ } else {
+ return typeHandler.parseRootImpl(value);
+ }
+ }
+
+ @Override
+ public FieldLoadedFinisher getValueFinisher() {
+ return VALUE_FINISHER;
+ }
+
+ @Override
+ public JsonTypeParser<?> asJsonTypeParser() {
+ return this;
+ }
+
+ public boolean isSubtyping() {
+ return isSubtyping;
+ }
+
+ private static final FieldLoadedFinisher VALUE_FINISHER = new FieldLoadedFinisher() {
+ @Override
+ Object getValueForUser(Object cachedValue) {
+ if (cachedValue == null) {
+ return null;
+ }
+ ObjectData data = (ObjectData) cachedValue;
+ return data.getProxy();
+ }
+ };
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/MethodHandler.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,12 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+/**
+ * An abstract method handler for {@link JsonInvocationHandler}.
+ */
+abstract class MethodHandler {
+ abstract Object handle(Object myself, ObjectData objectData, Object[] args) throws Throwable;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/ObjectData.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,71 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+import org.chromium.sdk.internal.protocolparser.JsonType;
+
+/**
+ * Stores all data for instance of json type.
+ * Each implementation of json type interface is a java dynamic proxy, that holds reference
+ * to {@link JsonInvocationHandler} which holds reference to this structure. ObjectData points
+ * back to dynamic proxy instance in {@link #proxy}.
+ */
+class ObjectData {
+
+ /**
+ * Stores type-specific set of pre-parsed fields.
+ */
+ private final Object[] fieldArray;
+
+ /**
+ * May be JSONObject (in most cases) or any
+ * object (for {@link JsonType#subtypesChosenManually()}=true).
+ */
+ private final Object underlyingObject;
+ private final TypeHandler<?> typeHandler;
+
+ /**
+ * Holds reference to base type object data (or null).
+ */
+ private final ObjectData superObjectData;
+ private Object proxy = null;
+
+ ObjectData(TypeHandler<?> typeHandler, Object inputObject, int fieldArraySize,
+ ObjectData superObjectData) {
+ this.superObjectData = superObjectData;
+ this.typeHandler = typeHandler;
+ this.underlyingObject = inputObject;
+
+ if (fieldArraySize == 0) {
+ fieldArray = null;
+ } else {
+ fieldArray = new Object[fieldArraySize];
+ }
+ }
+
+ void initProxy(Object proxy) {
+ this.proxy = proxy;
+ }
+
+ Object[] getFieldArray() {
+ return fieldArray;
+ }
+
+ Object getUnderlyingObject() {
+ return underlyingObject;
+ }
+
+ TypeHandler<?> getTypeHandler() {
+ return typeHandler;
+ }
+
+ ObjectData getSuperObjectData() {
+ return superObjectData;
+ }
+
+ Object getProxy() {
+ return proxy;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/QuickParser.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,43 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.protocolparser.JsonSubtypeCondition;
+
+/**
+ * A parser that accepts value of JSON field and outputs value in another form (e.g. string
+ * is converted to enum constant) to serve field getters in JsonType interfaces.
+ * The parser is called "quick" because it is supposed to employ only fast conversions.
+ * The quick parser should be suitable for subtype conditions
+ * (see {@link JsonSubtypeCondition} etc), because they should not take long to evaluate.
+ */
+abstract class QuickParser<T> extends SlowParser<T> {
+ @Override
+ public T parseValue(Object value, ObjectData thisData) throws JsonProtocolParseException {
+ return parseValueQuick(value);
+ }
+
+ /**
+ * Parses input value and returns output that doesn't need any post-processing
+ * by {@link FieldLoadedFinisher} (see {@link SlowParser}).
+ */
+ public abstract T parseValueQuick(Object value) throws JsonProtocolParseException;
+
+ @Override
+ public QuickParser<T> asQuickParser() {
+ return this;
+ }
+
+ @Override
+ public FieldLoadedFinisher getValueFinisher() {
+ return null;
+ }
+
+ @Override
+ public JsonTypeParser<?> asJsonTypeParser() {
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/RefToType.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,22 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+/**
+ * Late-resolvable reference to {@link TypeHandler}, for building {@link JsonTypeParser}.
+ */
+abstract class RefToType<T> {
+ /**
+ * Returns json type.
+ */
+ abstract Class<?> getTypeClass();
+
+ /**
+ * Returns {@link TypeHandler} corresponding to {@link #getTypeClass()}. The method becomes
+ * available only after cross-reference resolving has been finished in depths of
+ * {@link JsonProtocolParser} constructor.
+ */
+ abstract TypeHandler<T> get();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/SlowParser.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,30 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+
+/**
+ * A parser that accepts value of JSON field and outputs value in another form (e.g. string
+ * is converted to enum constant) to serve field getters in JsonType interfaces.
+ * <p>
+ * First the input value should be processed by {@link #parseValue(Object, ObjectData)} method
+ * that returns intermediate value (that may be stored in {@link ObjectData#getFieldArray()} array).
+ * Then the output value may be obtained via value post-processor, available
+ * from {@link #getValueFinisher()} (which is null in most cases, but not always).
+ * <p>The parser's name "slow" reads "may be slow". It means that parser may do heavy operations.
+ * Alternatively parser may be (optionally) castable to {@link QuickParser}
+ * via {@link #asQuickParser()} method.
+ */
+abstract class SlowParser<T> {
+ abstract T parseValue(Object value, ObjectData thisData) throws JsonProtocolParseException;
+
+ abstract FieldLoadedFinisher getValueFinisher();
+ abstract JsonTypeParser<?> asJsonTypeParser();
+
+ QuickParser<T> asQuickParser() {
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/SubtypeCaster.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,37 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+
+/**
+ * An internal facility for navigating from object of base type to object of subtype. Used only
+ * when user wants to parse JSON object as subtype.
+ * It works in terms of {@link ObjectData}.
+ */
+abstract class SubtypeCaster {
+ private final Class<?> baseType;
+ private final RefToType<?> subtypeRef;
+
+ SubtypeCaster(Class<?> baseType, RefToType<?> subtypeRef) {
+ this.baseType = baseType;
+ this.subtypeRef = subtypeRef;
+ }
+
+ abstract ObjectData getSubtypeObjectData(ObjectData baseObjectData)
+ throws JsonProtocolParseException;
+
+ Class<?> getSubtype() {
+ return subtypeRef.getTypeClass();
+ }
+
+ TypeHandler<?> getSubtypeHandler() {
+ return subtypeRef.get();
+ }
+
+ Class<?> getBaseType() {
+ return baseType;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,343 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.protocolparser.dynamicimpl;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.chromium.sdk.internal.protocolparser.JsonProtocolModelParseException;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.json.simple.JSONObject;
+
+/**
+ * The instance of this class corresponds to a particular json type. Primarily it serves
+ * as a factory for dynamic proxy/{@link ObjectData}, but also plays a role of type
+ * descriptor object.
+ */
+class TypeHandler<T> {
+ private final Class<T> typeClass;
+ private Constructor<? extends T> proxyClassConstructor = null;
+
+ /** Size of array that holds type-specific instance data. */
+ private final int fieldArraySize;
+
+ /** Method implementation for dynamic proxy. */
+ private final Map<Method, MethodHandler> methodHandlerMap;
+
+ /** Loaders that should read values and save them in field array on parse time. */
+ private final List<FieldLoader> fieldLoaders;
+
+ /** Set of parsers that non-lazyly check that all fields read OK. */
+ private final EagerFieldParser eagerFieldParser;
+
+ /** Holds the data about recognizing subtypes. */
+ private final AlgebraicCasesData algCasesData;
+
+ /** Full set of allowed field names. Should be used to check that JSON object is well-formed. */
+ private final Set<String> closedNameSet = null;
+
+ /** Subtype aspects of the type or null */
+ private final SubtypeAspect subtypeAspect;
+
+ TypeHandler(Class<T> typeClass, RefToType<?> jsonSuperClass, int fieldArraySize,
+ Map<Method, MethodHandler> methodHandlerMap,
+ List<FieldLoader> fieldLoaders,
+ List<FieldCondition> fieldConditions, EagerFieldParser eagerFieldParser,
+ AlgebraicCasesData algCasesData) {
+ this.typeClass = typeClass;
+ this.fieldArraySize = fieldArraySize;
+ this.methodHandlerMap = methodHandlerMap;
+ this.fieldLoaders = fieldLoaders;
+ this.eagerFieldParser = eagerFieldParser;
+ this.algCasesData = algCasesData;
+ if (jsonSuperClass == null) {
+ if (!fieldConditions.isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+ this.subtypeAspect = new AbsentSubtypeAspect();
+ } else {
+ this.subtypeAspect = new ExistingSubtypeAspect(jsonSuperClass, fieldConditions);
+ }
+ }
+
+ public Class<T> getTypeClass() {
+ return typeClass;
+ }
+
+ public ObjectData parse(Object input, ObjectData superObjectData)
+ throws JsonProtocolParseException {
+ try {
+ subtypeAspect.checkSuperObject(superObjectData);
+
+ Map<?, ?> jsonProperties = null;
+ if (input instanceof JSONObject) {
+ jsonProperties = (JSONObject) input;
+ }
+
+ ObjectData objectData = new ObjectData(this, input, fieldArraySize, superObjectData);
+ if (!fieldLoaders.isEmpty() && jsonProperties == null) {
+ throw new JsonProtocolParseException("JSON object input expected");
+ }
+
+ for (FieldLoader fieldLoader : fieldLoaders) {
+ String fieldName = fieldLoader.getFieldName();
+ Object value = jsonProperties.get(fieldName);
+ boolean hasValue;
+ if (value == null) {
+ hasValue = jsonProperties.containsKey(fieldName);
+ } else {
+ hasValue = true;
+ }
+ fieldLoader.parse(hasValue, value, objectData);
+ }
+
+ if (closedNameSet != null) {
+ for (Object fieldNameObject : jsonProperties.keySet()) {
+ if (!closedNameSet.contains(fieldNameObject)) {
+ throw new JsonProtocolParseException("Unexpected field " + fieldNameObject);
+ }
+ }
+ }
+
+ parseObjectSubtype(objectData, jsonProperties, input);
+
+ final boolean checkLazyParsedFields = false;
+ if (checkLazyParsedFields) {
+ eagerFieldParser.parseAllFields(objectData);
+ }
+ wrapInProxy(objectData, methodHandlerMap);
+ return objectData;
+ } catch (JsonProtocolParseException e) {
+ throw new JsonProtocolParseException("Failed to parse type " + getTypeClass().getName(), e);
+ }
+ }
+
+ public T parseRoot(Object input) throws JsonProtocolParseException {
+ ObjectData baseData = parseRootImpl(input);
+ return typeClass.cast(baseData.getProxy());
+ }
+
+ public ObjectData parseRootImpl(Object input) throws JsonProtocolParseException {
+ return subtypeAspect.parseFromSuper(input);
+ }
+
+ SubtypeSupport getSubtypeSupport() {
+ return subtypeAspect;
+ }
+
+ @SuppressWarnings("unchecked")
+ <S> TypeHandler<S> cast(Class<S> typeClass) {
+ if (this.typeClass != this.typeClass) {
+ throw new RuntimeException();
+ }
+ return (TypeHandler<S>)this;
+ }
+
+ static abstract class SubtypeSupport {
+ abstract void setSubtypeCaster(SubtypeCaster subtypeCaster)
+ throws JsonProtocolModelParseException;
+ abstract void checkHasSubtypeCaster() throws JsonProtocolModelParseException;
+ }
+
+ private void parseObjectSubtype(ObjectData objectData, Map<?, ?> jsonProperties, Object input)
+ throws JsonProtocolParseException {
+ if (algCasesData == null) {
+ return;
+ }
+ if (!algCasesData.isManualChoose()) {
+ if (jsonProperties == null) {
+ throw new JsonProtocolParseException(
+ "JSON object input expected for non-manual subtyping");
+ }
+ int code = -1;
+ for (int i = 0; i < algCasesData.getSubtypes().size(); i++) {
+ TypeHandler<?> nextSubtype = algCasesData.getSubtypes().get(i).get();
+ boolean ok = nextSubtype.subtypeAspect.checkConditions(jsonProperties);
+ if (ok) {
+ if (code == -1) {
+ code = i;
+ } else {
+ throw new JsonProtocolParseException("More than one case match");
+ }
+ }
+ }
+ if (code == -1) {
+ if (!algCasesData.hasDefaultCase()) {
+ throw new JsonProtocolParseException("Not a singe case matches");
+ }
+ } else {
+ ObjectData fieldData =
+ algCasesData.getSubtypes().get(code).get().parse(input, objectData);
+ objectData.getFieldArray()[algCasesData.getVariantValueFieldPos()] = fieldData;
+ }
+ objectData.getFieldArray()[algCasesData.getVariantCodeFieldPos()] =
+ Integer.valueOf(code);
+ }
+ }
+
+ /**
+ * Encapsulate subtype aspects of the type.
+ */
+ private static abstract class SubtypeAspect extends SubtypeSupport {
+ abstract void checkSuperObject(ObjectData superObjectData) throws JsonProtocolParseException;
+ abstract ObjectData parseFromSuper(Object input) throws JsonProtocolParseException;
+ abstract boolean checkConditions(Map<?, ?> jsonProperties) throws JsonProtocolParseException;
+ }
+
+ private class AbsentSubtypeAspect extends SubtypeAspect {
+ @Override
+ void checkSuperObject(ObjectData superObjectData) throws JsonProtocolParseException {
+ if (superObjectData != null) {
+ throw new JsonProtocolParseException("super object is not expected");
+ }
+ }
+ @Override
+ boolean checkConditions(Map<?, ?> jsonProperties) throws JsonProtocolParseException {
+ throw new JsonProtocolParseException("Not a subtype: " + typeClass.getName());
+ }
+ @Override
+ ObjectData parseFromSuper(Object input) throws JsonProtocolParseException {
+ return TypeHandler.this.parse(input, null);
+ }
+ @Override
+ void checkHasSubtypeCaster() {
+ }
+ @Override
+ void setSubtypeCaster(SubtypeCaster subtypeCaster) throws JsonProtocolModelParseException {
+ throw new JsonProtocolModelParseException("Not a subtype: " + typeClass.getName());
+ }
+ }
+
+ private class ExistingSubtypeAspect extends SubtypeAspect {
+ private final RefToType<?> jsonSuperClass;
+
+ /** Set of conditions that check whether this type conforms as subtype. */
+ private final List<FieldCondition> fieldConditions;
+
+ /** The helper that casts base type instance to instance of our type */
+ private SubtypeCaster subtypeCaster = null;
+
+ private ExistingSubtypeAspect(RefToType<?> jsonSuperClass,
+ List<FieldCondition> fieldConditions) {
+ this.jsonSuperClass = jsonSuperClass;
+ this.fieldConditions = fieldConditions;
+ }
+
+ @Override
+ boolean checkConditions(Map<?, ?> map) throws JsonProtocolParseException {
+ for (FieldCondition condition : fieldConditions) {
+ String name = condition.getPropertyName();
+ Object value = map.get(name);
+ boolean hasValue;
+ if (value == null) {
+ hasValue = map.containsKey(name);
+ } else {
+ hasValue = true;
+ }
+ boolean conditionRes = condition.checkValue(hasValue, value);
+ if (!conditionRes) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ void checkSuperObject(ObjectData superObjectData) throws JsonProtocolParseException {
+ if (jsonSuperClass == null) {
+ return;
+ }
+ if (!jsonSuperClass.getTypeClass().isAssignableFrom(
+ superObjectData.getTypeHandler().getTypeClass())) {
+ throw new JsonProtocolParseException("Unexpected type of super object");
+ }
+ }
+
+ @Override
+ ObjectData parseFromSuper(Object input) throws JsonProtocolParseException {
+ ObjectData base = jsonSuperClass.get().parseRootImpl(input);
+ ObjectData subtypeObject = subtypeCaster.getSubtypeObjectData(base);
+ if (subtypeObject == null) {
+ throw new JsonProtocolParseException("Failed to get subtype object while parsing");
+ }
+ return subtypeObject;
+ }
+ @Override
+ void checkHasSubtypeCaster() throws JsonProtocolModelParseException {
+ if (this.subtypeCaster == null) {
+ throw new JsonProtocolModelParseException("Subtype caster should have been set in " +
+ typeClass.getName());
+ }
+ }
+
+ @Override
+ void setSubtypeCaster(SubtypeCaster subtypeCaster) throws JsonProtocolModelParseException {
+ if (jsonSuperClass == null) {
+ throw new JsonProtocolModelParseException(typeClass.getName() +
+ " does not have supertype declared" +
+ " (accessed from " + subtypeCaster.getBaseType().getName() + ")");
+ }
+ if (subtypeCaster.getBaseType() != jsonSuperClass.getTypeClass()) {
+ throw new JsonProtocolModelParseException("Wrong base type in " + typeClass.getName() +
+ ", expected " + subtypeCaster.getBaseType().getName());
+ }
+ if (subtypeCaster.getSubtype() != typeClass) {
+ throw new JsonProtocolModelParseException("Wrong subtype");
+ }
+ if (this.subtypeCaster != null) {
+ throw new JsonProtocolModelParseException("Subtype caster is already set");
+ }
+ this.subtypeCaster = subtypeCaster;
+ }
+ }
+
+ private void wrapInProxy(ObjectData data, Map<Method, MethodHandler> methodHandlerMap) {
+ InvocationHandler handler = new JsonInvocationHandler(data, methodHandlerMap);
+ T proxy = createProxy(handler);
+ data.initProxy(proxy);
+ }
+
+ @SuppressWarnings("unchecked")
+ private T createProxy(InvocationHandler invocationHandler) {
+ if (proxyClassConstructor == null) {
+ Class<?>[] interfaces = new Class<?>[] { typeClass };
+ Class<?> proxyClass = Proxy.getProxyClass(getClass().getClassLoader(), interfaces);
+ Constructor<?> c;
+ try {
+ c = proxyClass.getConstructor(InvocationHandler.class);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ proxyClassConstructor = (Constructor<? extends T>) c;
+ }
+ try {
+ return proxyClassConstructor.newInstance(invocationHandler);
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static abstract class EagerFieldParser {
+ abstract void parseAllFields(ObjectData objectData) throws JsonProtocolParseException;
+ }
+
+ static abstract class AlgebraicCasesData {
+ abstract int getVariantCodeFieldPos();
+ abstract int getVariantValueFieldPos();
+ abstract boolean hasDefaultCase();
+ abstract List<RefToType<?>> getSubtypes();
+ abstract boolean isManualChoose();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/ChromeDevToolsProtocol.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,21 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools;
+
+/**
+ * Fields of a ChromeDevTools protocol message.
+ */
+public enum ChromeDevToolsProtocol {
+ COMMAND("command"),
+ DATA("data"),
+ RESULT("result"),
+ ;
+
+ public final String key;
+
+ private ChromeDevToolsProtocol(String key) {
+ this.key = key;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/ToolHandler.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,35 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools;
+
+import org.chromium.sdk.internal.transport.Message;
+
+/**
+ * An interface of a tool handler to which responses from certain tools are
+ * dispatched.
+ */
+public interface ToolHandler {
+
+ /**
+ * Handles message from a certain tool.
+ * Invoked from Dispatch thread (connection-driven).
+ *
+ * @param message to handle. Never null
+ */
+ void handleMessage(Message message);
+
+ /**
+ * Handles EOS (end-of-stream) message. Should not be called twice. Implementation
+ * may rely on this in its clean-up work.
+ * Invoked from Dispatch thread (connection-driven).
+ */
+ void handleEos();
+
+ /**
+ * Gets invoked when the debugger has detached from a browser instance (due to
+ * the connection loss or a user request).
+ */
+ void onDebuggerDetached();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/ToolName.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,40 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Known ChromeDevTools protocol tool names.
+ */
+public enum ToolName {
+
+ DEVTOOLS_SERVICE("DevToolsService"),
+ V8_DEBUGGER("V8Debugger"),
+ ;
+
+ private static final Map<String, ToolName> map =
+ new HashMap<String, ToolName>();
+
+ static {
+ for (ToolName name : values()) {
+ map.put(name.value, name);
+ }
+ }
+
+ public static ToolName forString(String value) {
+ if (value == null) {
+ return null;
+ }
+ return map.get(value);
+ }
+
+ public final String value;
+
+ private ToolName(String value) {
+ this.value = value;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/ToolOutput.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,21 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools;
+
+import org.chromium.sdk.internal.transport.Connection;
+
+/**
+ * Tool handler sends out its messages via this interface. This way tool handler does not
+ * need to access {@link Connection}. Instance of {@link ToolOutput} should add a proper
+ * header (with tool name and destination fields).
+ * This interface is used by a tool handler implementation.
+ */
+public interface ToolOutput {
+ /**
+ * Sends text message to a remote tool.
+ * @param content of message (without header)
+ */
+ void send(String content);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/devtools/DevToolsServiceCommand.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,40 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.devtools;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Known DevToolsService tool commands.
+ */
+public enum DevToolsServiceCommand {
+ PING("ping"),
+ VERSION("version"),
+ LIST_TABS("list_tabs"),
+ ;
+
+ private static Map<String, DevToolsServiceCommand> map =
+ new HashMap<String, DevToolsServiceCommand>();
+
+ static {
+ for (DevToolsServiceCommand command : values()) {
+ map.put(command.commandName, command);
+ }
+ }
+
+ public final String commandName;
+
+ private DevToolsServiceCommand(String commandName) {
+ this.commandName = commandName;
+ }
+
+ public static DevToolsServiceCommand forString(String commandName) {
+ if (commandName == null) {
+ return null;
+ }
+ return map.get(commandName);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,251 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.devtools;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.chromium.sdk.internal.JsonUtil;
+import org.chromium.sdk.internal.tools.ChromeDevToolsProtocol;
+import org.chromium.sdk.internal.tools.ToolHandler;
+import org.chromium.sdk.internal.tools.ToolOutput;
+import org.chromium.sdk.internal.transport.Message;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.ParseException;
+
+/**
+ * Handles the interaction with the "DevToolsService" tool.
+ */
+public class DevToolsServiceHandler implements ToolHandler {
+
+ /**
+ * The debugger connection to use.
+ */
+ private final ToolOutput toolOutput;
+
+ /**
+ * A "list_tabs" command callback. Is accessed in a synchronized way.
+ */
+ private ListTabsCallback listTabsCallback;
+
+ /**
+ * A "version" command callback. Is accessed in a synchronized way.
+ */
+ private VersionCallback versionCallback;
+
+ /**
+ * An access/modification lock for the callback fields.
+ */
+ private final Object lock = new Object();
+
+ public static class TabIdAndUrl {
+ public final int id;
+
+ public final String url;
+
+ private TabIdAndUrl(int id, String url) {
+ this.id = id;
+ this.url = url;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append('[')
+ .append(id)
+ .append('=')
+ .append(url)
+ .append(']')
+ .toString();
+ }
+ }
+
+ /**
+ * A callback that will be invoked when the ChromeDevTools protocol version
+ * is available.
+ */
+ private interface VersionCallback {
+ void versionReceived(String versionString);
+ }
+
+ /**
+ * A callback that will be invoked when the tabs from the associated browser
+ * instance are ready (or not...)
+ */
+ private interface ListTabsCallback {
+ void tabsReceived(List<TabIdAndUrl> tabs);
+
+ void failure(int result);
+ }
+
+ public DevToolsServiceHandler(ToolOutput toolOutput) {
+ this.toolOutput = toolOutput;
+ }
+
+ public void onDebuggerDetached() {
+ }
+
+ public void handleMessage(Message message) {
+ JSONObject json;
+ try {
+ json = JsonUtil.jsonObjectFromJson(message.getContent());
+ } catch (ParseException e) {
+ Logger.getLogger(DevToolsServiceHandler.class.getName()).log(
+ Level.SEVERE, "Invalid JSON received: {0}", message.getContent());
+ return;
+ }
+ String commandString = JsonUtil.getAsString(json, ChromeDevToolsProtocol.COMMAND.key);
+ DevToolsServiceCommand command = DevToolsServiceCommand.forString(commandString);
+ if (command != null) {
+ switch (command) {
+ case LIST_TABS:
+ handleListTabs(json);
+ break;
+ case VERSION:
+ handleVersion(json);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ public void handleEos() {
+ // ignore this event, we do not close browser in any way; but clients should dismiss
+ // all tickets
+ }
+
+ private void handleVersion(JSONObject json) {
+ VersionCallback callback;
+ synchronized (lock) {
+ callback = versionCallback;
+ versionCallback = null;
+ }
+ if (callback != null) {
+ String versionString = JsonUtil.getAsString(json, ChromeDevToolsProtocol.DATA.key);
+ callback.versionReceived(versionString);
+ }
+ }
+
+ private void handleListTabs(JSONObject json) {
+ ListTabsCallback callback;
+ synchronized (lock) {
+ callback = listTabsCallback;
+ listTabsCallback = null;
+ }
+ if (callback != null) {
+ int result = JsonUtil.getAsLong(json, ChromeDevToolsProtocol.RESULT.key).intValue();
+ if (result != 0) {
+ callback.failure(result);
+ return;
+ }
+ JSONArray data = JsonUtil.getAsJSONArray(json, ChromeDevToolsProtocol.DATA.key);
+ List<TabIdAndUrl> tabs = new ArrayList<TabIdAndUrl>(data.size());
+ for (int i = 0; i < data.size(); ++i) {
+ JSONArray idAndUrl = (JSONArray) data.get(i);
+ int id = ((Long) idAndUrl.get(0)).intValue();
+ String url = (String) idAndUrl.get(1);
+ tabs.add(new TabIdAndUrl(id, url));
+ }
+ callback.tabsReceived(tabs);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<TabIdAndUrl> listTabs(int timeout) {
+ final Semaphore sem = new Semaphore(0);
+ final List<TabIdAndUrl>[] output = new List[1];
+ synchronized (lock) {
+ if (listTabsCallback != null) {
+ throw new IllegalStateException("list_tabs request is pending");
+ }
+ listTabsCallback = new ListTabsCallback() {
+ public void failure(int result) {
+ sem.release();
+ }
+
+ public void tabsReceived(List<TabIdAndUrl> tabs) {
+ output[0] = tabs;
+ sem.release();
+ }
+ };
+ }
+ toolOutput.send(CommandFactory.listTabs());
+ try {
+ if (!sem.tryAcquire(timeout, TimeUnit.MILLISECONDS)) {
+ resetListTabsHandler();
+ }
+ } catch (InterruptedException e) {
+ // Fall through
+ }
+
+ if (output[0] == null) {
+ return Collections.emptyList();
+ }
+
+ return output[0];
+ }
+
+ public String version(int timeout) throws TimeoutException {
+ final Semaphore sem = new Semaphore(0);
+ final String[] output = new String[1];
+ synchronized (lock) {
+ if (versionCallback != null) {
+ throw new IllegalStateException("version request is pending");
+ }
+ versionCallback = new VersionCallback() {
+ public void versionReceived(String versionString) {
+ output[0] = versionString;
+ sem.release();
+ }
+ };
+ }
+ toolOutput.send(CommandFactory.version());
+ boolean res;
+ try {
+ res = sem.tryAcquire(timeout, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ if (!res) {
+ throw new TimeoutException("Failed to get version response in " + timeout + " ms");
+ }
+ return output[0];
+ }
+
+ /**
+ * This can get called asynchronously.
+ */
+ public void resetListTabsHandler() {
+ synchronized (lock) {
+ listTabsCallback = null;
+ }
+ }
+
+ private static class CommandFactory {
+
+ public static String ping() {
+ return createDevToolsMessage(DevToolsServiceCommand.PING);
+ }
+
+ public static String version() {
+ return createDevToolsMessage(DevToolsServiceCommand.VERSION);
+ }
+ public static String listTabs() {
+ return createDevToolsMessage(DevToolsServiceCommand.LIST_TABS);
+ }
+
+ private static String createDevToolsMessage(DevToolsServiceCommand command) {
+ return "{\"command\":" + JsonUtil.quoteString(command.commandName) + "}";
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/BreakpointImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,135 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8;
+
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.BrowserTab;
+
+/**
+ * A generic implementation of the Breakpoint interface.
+ */
+public class BreakpointImpl implements Breakpoint {
+
+ /**
+ * The breakpoint type.
+ */
+ private final Type type;
+
+ /**
+ * The breakpoint id as reported by the JavaScript VM.
+ */
+ private long id;
+
+ /**
+ * Whether the breakpoint is enabled.
+ */
+ private boolean isEnabled;
+
+ /**
+ * The number of times the breakpoint should be ignored
+ * by the JavaScript VM until it fires.
+ */
+ private int ignoreCount;
+
+ /**
+ * The breakpoint condition (plain JavaScript) that should be {@code true}
+ * for the breakpoint to fire.
+ */
+ private String condition;
+
+ /**
+ * The breakpoint manager that manages this breakpoint.
+ */
+ private final BreakpointManager breakpointManager;
+
+ /**
+ * Whether the breakpoint data have changed with respect
+ * to the JavaScript VM data.
+ */
+ private volatile boolean isDirty = false;
+
+ public BreakpointImpl(Type type, long id, boolean enabled, int ignoreCount, String condition,
+ BreakpointManager breakpointManager) {
+ this.type = type;
+ this.id = id;
+ this.isEnabled = enabled;
+ this.ignoreCount = ignoreCount;
+ this.condition = condition;
+ this.breakpointManager = breakpointManager;
+ }
+
+ public boolean isEnabled() {
+ return isEnabled;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public int getIgnoreCount() {
+ return ignoreCount;
+ }
+
+ public String getCondition() {
+ return condition;
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (this.isEnabled != enabled) {
+ setDirty(true);
+ }
+ this.isEnabled = enabled;
+ }
+
+ public void setIgnoreCount(int ignoreCount) {
+ if (this.ignoreCount != ignoreCount) {
+ setDirty(true);
+ }
+ this.ignoreCount = ignoreCount;
+ }
+
+
+ public void setCondition(String condition) {
+ if (!eq(this.condition, condition)) {
+ setDirty(true);
+ }
+ this.condition = condition;
+ }
+
+ private boolean eq(Object left, Object right) {
+ return left == right || (left != null && left.equals(right));
+ }
+
+ public void clear(final BrowserTab.BreakpointCallback callback) {
+ breakpointManager.clearBreakpoint(this, callback);
+ // The order must be preserved, otherwise the breakpointProcessor will not be able
+ // to identify the original breakpoint ID.
+ this.id = INVALID_ID;
+ }
+
+ public void flush(final BrowserTab.BreakpointCallback callback) {
+ if (!isDirty()) {
+ if (callback != null) {
+ callback.success(this);
+ }
+ return;
+ }
+ breakpointManager.changeBreakpoint(this, callback);
+ setDirty(false);
+ }
+
+ private void setDirty(boolean isDirty) {
+ this.isDirty = isDirty;
+ }
+
+ private boolean isDirty() {
+ return isDirty;
+ }
+
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/BreakpointManager.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,135 @@
+package org.chromium.sdk.internal.tools.v8;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.BrowserTab;
+import org.chromium.sdk.JavascriptVm.BreakpointCallback;
+import org.chromium.sdk.internal.DebugSession;
+import org.chromium.sdk.internal.protocol.BreakpointBody;
+import org.chromium.sdk.internal.protocol.CommandResponse;
+import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory;
+
+public class BreakpointManager {
+ /**
+ * This map shall contain only breakpoints with valid IDs.
+ */
+ private final Map<Long, Breakpoint> idToBreakpoint = new HashMap<Long, Breakpoint>();
+
+ private final DebugSession debugSession;
+
+ public BreakpointManager(DebugSession debugSession) {
+ this.debugSession = debugSession;
+ }
+
+ public void setBreakpoint(final Breakpoint.Type type, String target, int line, int position,
+ final boolean enabled, final String condition, final int ignoreCount,
+ final BrowserTab.BreakpointCallback callback) {
+ debugSession.sendMessageAsync(
+ DebuggerMessageFactory.setBreakpoint(type, target, toNullableInteger(line),
+ toNullableInteger(position), enabled, condition,
+ toNullableInteger(ignoreCount)),
+ true,
+ callback == null
+ ? null
+ : new V8CommandProcessor.V8HandlerCallback() {
+ public void messageReceived(CommandResponse response) {
+ SuccessCommandResponse successResponse = response.asSuccess();
+ if (successResponse != null) {
+ BreakpointBody body;
+ try {
+ body = successResponse.getBody().asBreakpointBody();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ long id = body.getBreakpoint();
+
+ final BreakpointImpl breakpoint =
+ new BreakpointImpl(type, id, enabled, ignoreCount,
+ condition, BreakpointManager.this);
+
+ callback.success(breakpoint);
+ idToBreakpoint.put(breakpoint.getId(), breakpoint);
+ } else {
+ callback.failure(response.asFailure().getMessage());
+ }
+ }
+ public void failure(String message) {
+ if (callback != null) {
+ callback.failure(message);
+ }
+ }
+ },
+ null);
+ }
+
+ public Breakpoint getBreakpoint(Long id) {
+ return idToBreakpoint.get(id);
+ }
+
+ public void clearBreakpoint(
+ final BreakpointImpl breakpointImpl, final BreakpointCallback callback) {
+ long id = breakpointImpl.getId();
+ if (id == Breakpoint.INVALID_ID) {
+ return;
+ }
+ idToBreakpoint.remove(id);
+ debugSession.sendMessageAsync(
+ DebuggerMessageFactory.clearBreakpoint(breakpointImpl),
+ true,
+ new V8CommandProcessor.V8HandlerCallback() {
+ public void messageReceived(CommandResponse response) {
+ SuccessCommandResponse successResponse = response.asSuccess();
+ if (successResponse != null) {
+ if (callback != null) {
+ callback.success(null);
+ }
+ } else {
+ if (callback != null) {
+ callback.failure(response.asFailure().getMessage());
+ }
+ }
+ }
+ public void failure(String message) {
+ if (callback != null) {
+ callback.failure(message);
+ }
+ }
+ },
+ null);
+ }
+
+ public void changeBreakpoint(final BreakpointImpl breakpointImpl,
+ final BreakpointCallback callback) {
+ debugSession.sendMessageAsync(
+ DebuggerMessageFactory.changeBreakpoint(breakpointImpl),
+ true,
+ new V8CommandProcessor.V8HandlerCallback() {
+ public void messageReceived(CommandResponse response) {
+ if (callback != null) {
+ SuccessCommandResponse successResponse = response.asSuccess();
+ if (successResponse != null) {
+ callback.success(breakpointImpl);
+ } else {
+ callback.failure(response.asFailure().getMessage());
+ }
+ }
+ }
+ public void failure(String message) {
+ if (callback != null) {
+ callback.failure(message);
+ }
+ }
+ },
+ null);
+ }
+
+ private static Integer toNullableInteger(int value) {
+ return value == Breakpoint.EMPTY_VALUE
+ ? null
+ : value;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,477 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8;
+
+import java.util.EnumSet;
+import java.util.Set;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.chromium.sdk.DebugEventListener;
+import org.chromium.sdk.TabDebugEventListener;
+import org.chromium.sdk.internal.BrowserImpl;
+import org.chromium.sdk.internal.BrowserTabImpl;
+import org.chromium.sdk.internal.DebugSession;
+import org.chromium.sdk.internal.DebugSessionManager;
+import org.chromium.sdk.internal.JsonUtil;
+import org.chromium.sdk.internal.Result;
+import org.chromium.sdk.internal.V8ContextFilter;
+import org.chromium.sdk.internal.protocol.data.ContextData;
+import org.chromium.sdk.internal.protocol.data.ContextHandle;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.ChromeDevToolsProtocol;
+import org.chromium.sdk.internal.tools.ToolHandler;
+import org.chromium.sdk.internal.tools.ToolOutput;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage;
+import org.chromium.sdk.internal.transport.Message;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.ParseException;
+
+/**
+ * Handles the interaction with the "V8Debugger" tool.
+ */
+public class ChromeDevToolSessionManager implements DebugSessionManager {
+
+ /**
+ * This exception is thrown whenever the handler could not get a tab
+ * attachment result from the debugged browser.
+ */
+ public static class AttachmentFailureException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public AttachmentFailureException() {
+ }
+
+ public AttachmentFailureException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+ /**
+ * An interface to run callbacks in response to V8 debugger commands that do
+ * not have associated JSON payloads.
+ */
+ public interface ResultAwareCallback {
+
+ /**
+ * This method is invoked whenever a response to a V8 command is received.
+ *
+ * @param result of the command
+ */
+ void resultReceived(Result result);
+ }
+
+ /** The class logger. */
+ private static final Logger LOGGER =
+ Logger.getLogger(ChromeDevToolSessionManager.class.getName());
+
+ private static final V8ContextFilter CONTEXT_FILTER = new V8ContextFilter() {
+ public boolean isContextOurs(ContextHandle contextHandle) {
+ Object data = contextHandle.data();
+ if (data == null) {
+ return false;
+ }
+
+ final boolean skipSketchCode = true;
+ if (skipSketchCode) {
+ // We do not actually have a context id to compare with. So we shouldn't waste time
+ // on parsing until we have this id.
+ return true;
+ }
+
+ long scriptContextId;
+ if (data instanceof String) {
+ String stringData = (String) data;
+ // we should parse string and check context id. It should have the format "type,id".
+ } else if (data instanceof JSONObject) {
+ JSONObject dataObject = (JSONObject) data;
+ ContextData contextData;
+ try {
+ contextData = V8ProtocolUtil.getV8Parser().parse(dataObject, ContextData.class);
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ scriptContextId = contextData.value();
+ }
+ //TODO(peter.rybin): Here we are probably supposed to compare it with our context id.
+ return true;
+ }
+ };
+
+ /** The host BrowserTabImpl instance. */
+ private final BrowserTabImpl browserTabImpl;
+
+ private final ToolOutput toolOutput;
+
+ /** The debug context for this handler. */
+ private final DebugSession debugSession;
+
+ private final AtomicReference<AttachState> attachState =
+ new AtomicReference<AttachState>(null);
+
+ private final AtomicReference<ResultAwareCallback> attachCallback =
+ new AtomicReference<ResultAwareCallback>(null);
+
+ private final AtomicReference<ResultAwareCallback> detachCallback =
+ new AtomicReference<ResultAwareCallback>(null);
+
+ /**
+ * A no-op JavaScript to evaluate.
+ */
+ public static final String JAVASCRIPT_VOID = "javascript:void(0);";
+
+ public ChromeDevToolSessionManager(BrowserTabImpl browserTabImpl, ToolOutput toolOutput) {
+ this.browserTabImpl = browserTabImpl;
+ this.toolOutput = toolOutput;
+ V8CommandOutputImpl v8MessageOutput = new V8CommandOutputImpl(toolOutput);
+ this.debugSession = new DebugSession(this, CONTEXT_FILTER, v8MessageOutput);
+ }
+
+ public DebugSession getDebugSession() {
+ return debugSession;
+ }
+
+ public DebugEventListener getDebugEventListener() {
+ return browserTabImpl.getDebugEventListener();
+ }
+
+ private TabDebugEventListener getTabDebugEventListener() {
+ return browserTabImpl.getTabDebugEventListener();
+ }
+
+ private void handleChromeDevToolMessage(final Message message) {
+ JSONObject json;
+ try {
+ json = JsonUtil.jsonObjectFromJson(message.getContent());
+ } catch (ParseException e) {
+ LOGGER.log(Level.SEVERE, "Invalid JSON received: {0}", message.getContent());
+ return;
+ }
+ String commandString = JsonUtil.getAsString(json, ChromeDevToolsProtocol.COMMAND.key);
+ DebuggerToolCommand command = DebuggerToolCommand.forName(commandString);
+ if (command != null) {
+ switch (command) {
+ case ATTACH:
+ processAttach(json);
+ break;
+ case DETACH:
+ processDetach(json);
+ break;
+ case DEBUGGER_COMMAND:
+ processDebuggerCommand(json);
+ break;
+ case NAVIGATED:
+ processNavigated(json);
+ break;
+ case CLOSED:
+ processClosed(json);
+ break;
+ }
+ return;
+ }
+ throw new IllegalArgumentException("Invalid command: " + commandString);
+ }
+
+ public void onDebuggerDetached() {
+ // ignore
+ }
+
+ /**
+ * Disconnects tab from connections. Can safely be called several times. This sends EOS
+ * message and unregisters tab from browser.
+ * Should be called from UI, may block.
+ */
+ public void cutTheLineMyself() {
+ toolHandler.cutTheLine();
+ }
+ /**
+ * This method is sure to be called only once.
+ */
+ private void handleEos() {
+ // We overwrite other values; nobody else should become unhappy, all expecters
+ // are in handle* methods and they are not going to get their results
+ attachState.set(AttachState.DISCONNECTED);
+ browserTabImpl.handleEosFromToolService();
+ debugSession.getV8CommandProcessor().processEos();
+
+ DebugEventListener debugEventListener = getDebugEventListener();
+ if (debugEventListener != null) {
+ debugEventListener.disconnected();
+ }
+ }
+
+ private AttachState getAttachState() {
+ return attachState.get();
+ }
+
+ /**
+ * This method is for UI -- pretty low precision of result type.
+ * @return whether the handler is attached to a tab
+ */
+ public boolean isAttachedForUi() {
+ return STATES_CALLED_ATTACHED.contains(getAttachState());
+ }
+
+ /**
+ * Attaches the remote debugger to the associated browser tab.
+ *
+ * @return the attachment result
+ * @throws AttachmentFailureException whenever the handler could not connect
+ * to the browser
+ */
+ public Result attachToTab() throws AttachmentFailureException {
+ boolean res = attachState.compareAndSet(null, AttachState.ATTACHING);
+ if (!res) {
+ throw new AttachmentFailureException("Illegal state", null);
+ }
+
+ String command = V8DebuggerToolMessageFactory.attach();
+ Result attachResult = sendSimpleCommandSync(attachCallback, command);
+
+ debugSession.startCommunication();
+
+ return attachResult;
+ }
+
+ /**
+ * Detaches the remote debugger from the associated browser tab.
+ * Should be called from UI thread.
+ * @return the detachment result
+ */
+ public Result detachFromTab() {
+ if (attachState.get() != AttachState.NORMAL) {
+ toolHandler.onDebuggerDetached();
+ return Result.ILLEGAL_TAB_STATE;
+ }
+
+ String command = V8DebuggerToolMessageFactory.detach();
+ Result result;
+ try {
+ result = sendSimpleCommandSync(detachCallback, command);
+ } catch (AttachmentFailureException e) {
+ result = null;
+ } finally {
+ // Make sure line is cut
+ cutTheLineMyself();
+ }
+
+ return result;
+ }
+
+ private Result sendSimpleCommandSync(AtomicReference<ResultAwareCallback> callbackReference,
+ String command) throws AttachmentFailureException {
+ final Semaphore sem = new Semaphore(0);
+ final Result[] output = new Result[1];
+ ResultAwareCallback callback = new ResultAwareCallback() {
+ public void resultReceived(Result result) {
+ output[0] = result;
+ sem.release();
+ }
+ };
+
+ boolean res = callbackReference.compareAndSet(null, callback);
+ if (!res) {
+ throw new IllegalStateException("Callback is already set");
+ }
+
+ boolean completed;
+ try {
+ toolOutput.send(command);
+
+ try {
+ completed = sem.tryAcquire(BrowserImpl.OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ } finally {
+ // Make sure we do not leave our callback behind us.
+ callbackReference.compareAndSet(callback, null);
+ }
+
+ // If the command fails, notify the caller.
+ if (!completed) {
+ throw new AttachmentFailureException("Timed out", null);
+ }
+
+ return output[0];
+ }
+
+ public ToolHandler getToolHandler() {
+ return toolHandler;
+ }
+
+ private final ToolHandlerImpl toolHandler = new ToolHandlerImpl();
+
+ private class ToolHandlerImpl implements ToolHandler {
+ private volatile boolean isLineCut = false;
+ private boolean alreadyHasEos = false;
+
+ /**
+ * Here we call methods that are normally are being invoked from Connection Dispatch
+ * thread. So we compete for "this" as synchronization monitor with that thread.
+ * We should be careful, cause theoretically it may cause a deadlock.
+ */
+ void cutTheLine() {
+ // First mark ourselves as "cut off" to stop other threads,
+ // then start waiting on synchronized.
+ isLineCut = true;
+ synchronized (this) {
+ sendEos();
+ }
+ }
+
+ public synchronized void handleMessage(Message message) {
+ if (isLineCut) {
+ return;
+ }
+ handleChromeDevToolMessage(message);
+ }
+
+ public synchronized void handleEos() {
+ if (isLineCut) {
+ return;
+ }
+ sendEos();
+ }
+ private synchronized void sendEos() {
+ if (alreadyHasEos) {
+ return;
+ }
+ alreadyHasEos = true;
+ ChromeDevToolSessionManager.this.handleEos();
+ }
+
+ public void onDebuggerDetached() {
+ // ignore
+ }
+ }
+
+ private void processClosed(JSONObject json) {
+ notifyCallback(detachCallback, Result.OK);
+ getTabDebugEventListener().closed();
+ cutTheLineMyself();
+ }
+
+ /**
+ * This method is invoked from synchronized code sections. It checks if there is a callback
+ * provided in {@code callbackReference}. Sets callback to null.
+ *
+ * @param callbackReference reference which may hold callback
+ * @param result to notify the callback with
+ */
+ private void notifyCallback(AtomicReference<ResultAwareCallback> callbackReference,
+ Result result) {
+ ResultAwareCallback callback = callbackReference.getAndSet(null);
+ if (callback != null) {
+ try {
+ callback.resultReceived(result);
+ } catch (Exception e) {
+ LOGGER.log(Level.WARNING, "Exception in the callback", e);
+ }
+ }
+ }
+
+ private void processAttach(JSONObject json) {
+ Long resultValue = JsonUtil.getAsLong(json, ChromeDevToolsProtocol.RESULT.key);
+ Result result = Result.forCode(resultValue.intValue());
+ // Message destination equals context.getTabId()
+ if (result == Result.OK) {
+ boolean res = attachState.compareAndSet(AttachState.ATTACHING, AttachState.NORMAL);
+ if (!res) {
+ throw new IllegalStateException();
+ }
+ } else {
+ if (result == null) {
+ result = Result.DEBUGGER_ERROR;
+ }
+ }
+ notifyCallback(attachCallback, result);
+ }
+
+ private void processDetach(JSONObject json) {
+ Long resultValue = JsonUtil.getAsLong(json, ChromeDevToolsProtocol.RESULT.key);
+ Result result = Result.forCode(resultValue.intValue());
+ if (result == Result.OK) {
+ // ignore result, we may already be in DISCONNECTED state
+ attachState.compareAndSet(AttachState.DETACHING, AttachState.DETACHED);
+ } else {
+ if (result == null) {
+ result = Result.DEBUGGER_ERROR;
+ }
+ }
+ notifyCallback(detachCallback, result);
+ }
+
+ private void processDebuggerCommand(JSONObject json) {
+ JSONObject v8Json = JsonUtil.getAsJSON(json, ChromeDevToolsProtocol.DATA.key);
+ V8CommandProcessor.checkNull(v8Json, "'data' field not found");
+ debugSession.getV8CommandProcessor().processIncomingJson(v8Json);
+ }
+
+ private void processNavigated(JSONObject json) {
+ String newUrl = JsonUtil.getAsString(json, ChromeDevToolsProtocol.DATA.key);
+ debugSession.navigated();
+ getTabDebugEventListener().navigated(newUrl);
+ }
+
+ public static class V8CommandOutputImpl implements V8CommandOutput {
+ private final ToolOutput toolOutput;
+
+ public V8CommandOutputImpl(ToolOutput toolOutput) {
+ this.toolOutput = toolOutput;
+ }
+
+ public void send(DebuggerMessage debuggerMessage, boolean isImmediate) {
+ toolOutput.send(
+ V8DebuggerToolMessageFactory.debuggerCommand(
+ JsonUtil.streamAwareToJson(debuggerMessage)));
+ if (isImmediate) {
+ toolOutput.send(
+ V8DebuggerToolMessageFactory.evaluateJavascript(JAVASCRIPT_VOID));
+ }
+ }
+ }
+
+ private static class V8DebuggerToolMessageFactory {
+
+ static String attach() {
+ return createDebuggerMessage(DebuggerToolCommand.ATTACH, null);
+ }
+
+ static String detach() {
+ return createDebuggerMessage(DebuggerToolCommand.DETACH, null);
+ }
+
+ public static String debuggerCommand(String json) {
+ return createDebuggerMessage(DebuggerToolCommand.DEBUGGER_COMMAND, json);
+ }
+
+ public static String evaluateJavascript(String javascript) {
+ return createDebuggerMessage(DebuggerToolCommand.EVALUATE_JAVASCRIPT,
+ JsonUtil.quoteString(javascript));
+ }
+
+ private static String createDebuggerMessage(
+ DebuggerToolCommand command, String dataField) {
+ StringBuilder sb = new StringBuilder("{\"command\":\"");
+ sb.append(command.commandName).append('"');
+ if (dataField != null) {
+ sb.append(",\"data\":").append(dataField);
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ private enum AttachState {
+ ATTACHING, NORMAL, DETACHING, DETACHED, DISCONNECTED
+ }
+
+ private static final Set<AttachState> STATES_CALLED_ATTACHED = EnumSet.of(AttachState.NORMAL);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/DebuggerCommand.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,60 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Known V8 VM debugger commands and events.
+ */
+public enum DebuggerCommand {
+ CONTINUE("continue"),
+ EVALUATE("evaluate"),
+ BACKTRACE("backtrace"),
+ FRAME("frame"),
+ SCRIPTS("scripts"),
+ SOURCE("source"),
+ SCOPE("scope"),
+ SETBREAKPOINT("setbreakpoint"),
+ CHANGEBREAKPOINT("changebreakpoint"),
+ CLEARBREAKPOINT("clearbreakpoint"),
+ LOOKUP("lookup"),
+ SUSPEND("suspend"),
+ VERSION("version"),
+
+ // Events
+ BREAK("break"),
+ EXCEPTION("exception"),
+ AFTER_COMPILE("afterCompile"),
+ ;
+
+ public final String value;
+
+ DebuggerCommand(String value) {
+ this.value = value;
+ }
+
+ private static final Map<String, DebuggerCommand> valueToCommandMap =
+ new HashMap<String, DebuggerCommand>();
+
+ static {
+ for (DebuggerCommand c : values()) {
+ valueToCommandMap.put(c.value, c);
+ }
+ }
+
+ /**
+ * @param value the DebuggerCommand string value
+ * @return the DebuggerCommand instance or null if none corresponds to value
+ */
+ public static DebuggerCommand forString(String value) {
+ if (value == null) {
+ return null;
+ }
+ return valueToCommandMap.get(value);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/DebuggerToolCommand.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,44 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Known V8Debugger tool commands.
+ */
+public enum DebuggerToolCommand {
+ ATTACH("attach"),
+ DETACH("detach"),
+ DEBUGGER_COMMAND("debugger_command"),
+ EVALUATE_JAVASCRIPT("evaluate_javascript"),
+
+ // Events
+ NAVIGATED("navigated"),
+ CLOSED("closed");
+
+ private static final Map<String, DebuggerToolCommand> map =
+ new HashMap<String, DebuggerToolCommand>();
+
+ static {
+ for (DebuggerToolCommand command : values()) {
+ map.put(command.commandName, command);
+ }
+ }
+
+ public final String commandName;
+
+ private DebuggerToolCommand(String value) {
+ this.commandName = value;
+ }
+
+ public static DebuggerToolCommand forName(String name) {
+ if (name == null) {
+ return null;
+ }
+ return map.get(name);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/DefaultResponseHandler.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,86 @@
+package org.chromium.sdk.internal.tools.v8;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.chromium.sdk.internal.DebugSession;
+import org.chromium.sdk.internal.protocol.EventNotification;
+import org.chromium.sdk.internal.protocol.IncomingMessage;
+import org.chromium.sdk.internal.tools.v8.processor.AfterCompileProcessor;
+import org.chromium.sdk.internal.tools.v8.processor.BreakpointProcessor;
+import org.chromium.sdk.internal.tools.v8.processor.V8EventProcessor;
+
+public class DefaultResponseHandler {
+
+ /** The class logger. */
+ private static final Logger LOGGER = Logger.getLogger(DefaultResponseHandler.class.getName());
+
+ /** The breakpoint processor. */
+ private final BreakpointProcessor bpp;
+
+ /** The "afterCompile" event processor. */
+ private final AfterCompileProcessor afterCompileProcessor;
+
+ public DefaultResponseHandler(DebugSession debugSession) {
+ this.bpp = new BreakpointProcessor(debugSession);
+ this.afterCompileProcessor = new AfterCompileProcessor(debugSession);
+ }
+
+ public BreakpointProcessor getBreakpointProcessor() {
+ return bpp;
+ }
+
+ /**
+ * @param type response type ("response" or "event")
+ * @param response from the V8 VM debugger
+ */
+ public void handleResponseWithHandler(IncomingMessage response) {
+ EventNotification eventResponse = response.asEventNotification();
+ if (eventResponse == null) {
+ // Currently only events are supported.
+ return;
+ }
+ String commandString = eventResponse.getEvent();
+ DebuggerCommand command = DebuggerCommand.forString(commandString);
+ if (command == null) {
+ LOGGER.log(Level.WARNING,
+ "Unknown command in V8 debugger reply JSON: {0}", commandString);
+ return;
+ }
+ final ProcessorGetter handlerGetter = command2EventProcessorGetter.get(command);
+ if (handlerGetter == null) {
+ return;
+ }
+ handlerGetter.get(this).messageReceived(eventResponse);
+ }
+
+ private static abstract class ProcessorGetter {
+ abstract V8EventProcessor get(DefaultResponseHandler instance);
+ }
+
+ /**
+ * The handlers that should be invoked when certain command responses arrive.
+ */
+ private static final Map<DebuggerCommand, ProcessorGetter> command2EventProcessorGetter;
+ static {
+ command2EventProcessorGetter = new HashMap<DebuggerCommand, ProcessorGetter>();
+ ProcessorGetter bppGetter = new ProcessorGetter() {
+ @Override
+ BreakpointProcessor get(DefaultResponseHandler instance) {
+ return instance.bpp;
+ }
+ };
+ command2EventProcessorGetter.put(DebuggerCommand.BREAK /* event */, bppGetter);
+ command2EventProcessorGetter.put(DebuggerCommand.EXCEPTION /* event */, bppGetter);
+
+ command2EventProcessorGetter.put(DebuggerCommand.AFTER_COMPILE /* event */,
+ new ProcessorGetter() {
+ @Override
+ AfterCompileProcessor get(DefaultResponseHandler instance) {
+ return instance.afterCompileProcessor;
+ }
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/MethodIsBlockingException.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,44 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8;
+
+/**
+ * Signals that a deadlock was about to happen.
+ * <p>
+ * Method may wait for some callback to receive a result from some worker
+ * thread. If this method happened to be called from another callback
+ * being run by the same thread this will block the thread forever because
+ * method is never going to get result. Such situation may raise this
+ * exception.
+ * <p>
+ * Currently this exception is never actually thrown so it gets more of
+ * symbolic sense. Nevertheless it's still important to keep its declaration
+ * because it helps to track which ones are blocking and which are not.
+ * To do this, one should temporary modify this class and make exception checked
+ * (make it extend {@code java.lang.Exception}). This will enforce the proper
+ * declaration of all blocking methods.
+ * <p>Here are the simple rules: you never normally catch this exception, but
+ * add throws declaration wherever needed; if your callback method needs to
+ * throw it you are doing something wrong (i.e. calling blocking method from
+ * a callback) and risk running into a deadlock; wherever you are
+ * sure you are on a safe ground (not called from callback) and there is no
+ * need in further tracking this exception, make a symbolic try/catch and
+ * explain why you think it's safe, in a catch block:
+ * <pre>
+ * try {
+ * makeSureAllScriptsAreLoaded();
+ * } catch (MethodIsBlockingException e) {
+ * // I'm being called from my own thread, so it's ok if method blocks,
+ * // I can wait
+ * throw new RuntimeException(e); // never executed
+ * }
+ * </pre>
+ * <p>By default, {@code MethodIsBlockingException} is unchecked exception,
+ * so you may completely ignore it.
+ */
+public class MethodIsBlockingException extends RuntimeException {
+ // We never actually instantiate this exception, because it's symbolic.
+ private MethodIsBlockingException() {}
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8BlockingCallback.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,34 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8;
+
+import org.chromium.sdk.internal.protocol.CommandResponse;
+import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
+
+/**
+ * The callback that handles JSON response to a VM command. The command-sender is staying
+ * blocked until callback finishes, which allows the callback to return a result of
+ * user-specified type {@code RES}.
+ * <p>User should subclass this and implement
+ * {@link #handleSuccessfulResponse(SuccessCommandResponse)} method.
+ * @param <RES> type of result value that is passed back to caller
+ */
+public abstract class V8BlockingCallback<RES> {
+ public RES messageReceived(CommandResponse response) {
+ SuccessCommandResponse successResponse = response.asSuccess();
+ if (successResponse == null) {
+ throw new RuntimeException("Unsuccessful command " +
+ response.asFailure().getMessage());
+ }
+ return handleSuccessfulResponse(successResponse);
+ }
+
+ /**
+ * User-implementable method that handled successful json response and pass result back to
+ * command-sender.
+ * @param response with "success=true"
+ */
+ protected abstract RES handleSuccessfulResponse(SuccessCommandResponse response);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8CommandOutput.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,16 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8;
+
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage;
+
+/**
+ * Abstract sink for DebuggerMessage v8 messages. It is responsible for sending them to a
+ * particular instance of V8 VM. For this end actual message may get additional fields or
+ * be reformatted.
+ */
+public interface V8CommandOutput {
+ void send(DebuggerMessage debuggerMessage, boolean immediate);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8CommandProcessor.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,200 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8;
+
+import java.util.Collection;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.chromium.sdk.SyncCallback;
+import org.chromium.sdk.internal.CloseableMap;
+import org.chromium.sdk.internal.protocol.CommandResponse;
+import org.chromium.sdk.internal.protocol.IncomingMessage;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage;
+import org.json.simple.JSONObject;
+
+/**
+ * Sends JSON commands to V8 VM and handles responses. Command is sent
+ * via {@code V8CommandOutput}. Response is passed back to callback if it was provided.
+ * Also all responses and events are dispatched to group of dedicated processors.
+ */
+public class V8CommandProcessor implements V8CommandSender<DebuggerMessage, RuntimeException> {
+
+ /**
+ * A callback to handle V8 debugger responses.
+ */
+ public interface V8HandlerCallback {
+ /**
+ * This method is invoked when a debugger command result has become
+ * available.
+ *
+ * @param response from the V8 debugger
+ */
+ void messageReceived(CommandResponse response);
+
+ /**
+ * This method is invoked when a debugger command has failed.
+ *
+ * @param message containing the failure reason
+ */
+ void failure(String message);
+
+ /** A no-op callback implementation. */
+ V8HandlerCallback NULL_CALLBACK = new V8HandlerCallback() {
+ public void failure(String message) {
+ }
+
+ public void messageReceived(CommandResponse response) {
+ }
+ };
+ }
+
+ /** The class logger. */
+ static final Logger LOGGER = Logger.getLogger(V8CommandProcessor.class.getName());
+
+ private final CloseableMap<Integer, CallbackEntry> callbackMap = CloseableMap.newLinkedMap();
+
+ private final V8CommandOutput messageOutput;
+
+ private final DefaultResponseHandler defaultResponseHandler;
+
+
+ public V8CommandProcessor(V8CommandOutput messageOutput,
+ DefaultResponseHandler defaultResponseHandler) {
+ this.messageOutput = messageOutput;
+ this.defaultResponseHandler = defaultResponseHandler;
+ }
+
+ public void sendV8CommandAsync(DebuggerMessage message, boolean isImmediate,
+ V8HandlerCallback v8HandlerCallback, SyncCallback syncCallback) {
+
+ if (v8HandlerCallback != null) {
+ // TODO(peter.rybin): should we handle IllegalStateException better than rethrowing it?
+ try {
+ callbackMap.put(message.getSeq(), new CallbackEntry(v8HandlerCallback,
+ syncCallback));
+ } catch (IllegalStateException e) {
+ throw new IllegalStateException("Connection is closed", e);
+ }
+ }
+ try {
+ messageOutput.send(message, isImmediate);
+ } catch (RuntimeException e) {
+ if (v8HandlerCallback != null) {
+ callbackMap.remove(message.getSeq());
+ }
+ throw e;
+ }
+ }
+
+ public void processIncomingJson(final JSONObject v8Json) {
+ IncomingMessage response;
+ try {
+ response = V8ProtocolUtil.getV8Parser().parse(v8Json, IncomingMessage.class);
+ } catch (JsonProtocolParseException e) {
+ LOGGER.log(Level.SEVERE, "JSON message does not conform to the protocol", e);
+ return;
+ }
+
+ final CommandResponse commandResponse = response.asCommandResponse();
+
+ if (commandResponse != null) {
+ int requestSeqInt = (int) commandResponse.getRequestSeq();
+ CallbackEntry callbackEntry = callbackMap.removeIfContains(requestSeqInt);
+ if (callbackEntry != null) {
+ LOGGER.log(
+ Level.INFO,
+ "Request-response roundtrip: {0}ms",
+ getCurrentMillis() - callbackEntry.commitMillis);
+
+ CallbackCaller caller = new CallbackCaller() {
+ @Override
+ void call(V8HandlerCallback handlerCallback) {
+ handlerCallback.messageReceived(commandResponse);
+ }
+ };
+ try {
+ callThemBack(callbackEntry, caller, requestSeqInt);
+ } catch (RuntimeException e) {
+ LOGGER.log(Level.SEVERE, "Failed to dispatch response to callback", e);
+ }
+ }
+ }
+
+ defaultResponseHandler.handleResponseWithHandler(response);
+ }
+
+ public void processEos() {
+ // We should call them in the order they have been submitted.
+ Collection<CallbackEntry> entries = callbackMap.close().values();
+ for (CallbackEntry entry : entries) {
+ callThemBack(entry, failureCaller, -1);
+ }
+ }
+
+ private static abstract class CallbackCaller {
+ abstract void call(V8HandlerCallback handlerCallback);
+ }
+
+ private final static CallbackCaller failureCaller = new CallbackCaller() {
+ @Override
+ void call(V8HandlerCallback handlerCallback) {
+ handlerCallback.failure("Detach");
+ }
+ };
+
+
+ private void callThemBack(CallbackEntry callbackEntry, CallbackCaller callbackCaller,
+ int requestSeq) {
+ RuntimeException callbackException = null;
+ try {
+ if (callbackEntry.v8HandlerCallback != null) {
+ LOGGER.log(
+ Level.INFO, "Notified debugger command callback, request_seq={0}", requestSeq);
+ callbackCaller.call(callbackEntry.v8HandlerCallback);
+ }
+ } catch (RuntimeException e) {
+ callbackException = e;
+ throw e;
+ } finally {
+ if (callbackEntry.syncCallback != null) {
+ callbackEntry.syncCallback.callbackDone(callbackException);
+ }
+ }
+ }
+
+ /**
+ * @return milliseconds since the epoch
+ */
+ private static long getCurrentMillis() {
+ return System.currentTimeMillis();
+ }
+
+ static void checkNull(Object object, String message) {
+ if (object == null) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+
+ private static class CallbackEntry {
+ final V8HandlerCallback v8HandlerCallback;
+
+ final SyncCallback syncCallback;
+
+ final long commitMillis;
+
+ CallbackEntry(V8HandlerCallback v8HandlerCallback, SyncCallback syncCallback) {
+ this.v8HandlerCallback = v8HandlerCallback;
+ this.commitMillis = getCurrentMillis();
+ this.syncCallback = syncCallback;
+ }
+ }
+
+ public void removeAllCallbacks() {
+ // TODO(peter.rybin): get rid of this method
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8CommandSender.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,18 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8;
+
+import org.chromium.sdk.SyncCallback;
+import org.chromium.sdk.internal.tools.v8.V8CommandProcessor.V8HandlerCallback;
+
+/**
+ * API to asynchronous message sender that supports callbacks.
+ * @param <MESSAGE> type of message supported
+ * @param <EX> exception that may be thrown synchronously.
+ */
+public interface V8CommandSender<MESSAGE, EX extends Exception> {
+ void sendV8CommandAsync(MESSAGE message, boolean isImmediate,
+ V8HandlerCallback v8HandlerCallback, SyncCallback syncCallback) throws EX;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8Helper.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,355 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.chromium.sdk.CallbackSemaphore;
+import org.chromium.sdk.SyncCallback;
+import org.chromium.sdk.JsValue.Type;
+import org.chromium.sdk.internal.DataWithRef;
+import org.chromium.sdk.internal.DebugSession;
+import org.chromium.sdk.internal.FunctionAdditionalProperties;
+import org.chromium.sdk.internal.JsDataTypeUtil;
+import org.chromium.sdk.internal.PropertyHoldingValueMirror;
+import org.chromium.sdk.internal.PropertyReference;
+import org.chromium.sdk.internal.ScopeMirror;
+import org.chromium.sdk.internal.ScriptManager;
+import org.chromium.sdk.internal.SubpropertiesMirror;
+import org.chromium.sdk.internal.ValueLoadException;
+import org.chromium.sdk.internal.ValueMirror;
+import org.chromium.sdk.internal.protocol.CommandResponse;
+import org.chromium.sdk.internal.protocol.FrameObject;
+import org.chromium.sdk.internal.protocol.ScopeRef;
+import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
+import org.chromium.sdk.internal.protocol.data.FunctionValueHandle;
+import org.chromium.sdk.internal.protocol.data.ObjectValueHandle;
+import org.chromium.sdk.internal.protocol.data.PropertyObject;
+import org.chromium.sdk.internal.protocol.data.RefWithDisplayData;
+import org.chromium.sdk.internal.protocol.data.ScriptHandle;
+import org.chromium.sdk.internal.protocol.data.ValueHandle;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory;
+import org.chromium.sdk.internal.tools.v8.request.ScriptsMessage;
+
+/**
+ * A helper class for performing complex V8-related operations.
+ */
+public class V8Helper {
+
+ /**
+ * The debug context in which the operations are performed.
+ */
+ private final DebugSession debugSession;
+
+ /**
+ * A semaphore that prevents concurrent script reloading (which may effectively
+ * double the efforts.)
+ */
+ private final Semaphore scriptsReloadSemaphore = new Semaphore(1);
+
+ public V8Helper(DebugSession debugSession) {
+ this.debugSession = debugSession;
+ }
+
+ /**
+ * Reloads all normal scripts found in the page. First, all scripts without
+ * their sources are retrieved to save bandwidth (script list change during a
+ * page lifetime is a relatively rare event.) If at least one script has been
+ * added, the script cache is dropped and re-populated with new scripts that
+ * are re-requested together with their sources.
+ *
+ * @param callback to invoke when the script reloading has completed
+ */
+ public void reloadAllScriptsAsync(V8CommandProcessor.V8HandlerCallback callback,
+ SyncCallback syncCallback) {
+ final V8CommandProcessor.V8HandlerCallback finalCallback = callback != null
+ ? callback
+ : V8CommandProcessor.V8HandlerCallback.NULL_CALLBACK;
+ lock();
+ debugSession.sendMessageAsync(
+ DebuggerMessageFactory.scripts(ScriptsMessage.SCRIPTS_NORMAL, true),
+ true,
+ new V8CommandProcessor.V8HandlerCallback() {
+ public void failure(String message) {
+ unlock();
+ finalCallback.failure(message);
+ }
+
+ public void messageReceived(CommandResponse response) {
+ SuccessCommandResponse successResponse = response.asSuccess();
+
+ // TODO(peter.rybin): add try/finally for unlock, with some error reporting probably.
+ List<ScriptHandle> body;
+ try {
+ body = successResponse.getBody().asScripts();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ ScriptManager scriptManager = debugSession.getScriptManager();
+ for (int i = 0; i < body.size(); ++i) {
+ ScriptHandle scriptHandle = body.get(i);
+ Long id = V8ProtocolUtil.getScriptIdFromResponse(scriptHandle);
+ if (scriptManager.findById(id) == null &&
+ !ChromeDevToolSessionManager.JAVASCRIPT_VOID.equals(scriptHandle.source())) {
+ scriptManager.addScript(
+ scriptHandle,
+ successResponse.getRefs());
+ }
+ }
+ unlock();
+ finalCallback.messageReceived(response);
+ }
+ },
+ syncCallback);
+ }
+
+ protected void lock() {
+ try {
+ scriptsReloadSemaphore.acquire();
+ } catch (InterruptedException e) {
+ // consider it a successful acquisition
+ }
+ }
+
+ protected void unlock() {
+ scriptsReloadSemaphore.release();
+ }
+
+ /**
+ * Gets all resolved locals for the call frame, caches scripts and objects in
+ * the scriptManager and handleManager.
+ *
+ * @param frame to get the data for
+ * @return the mirrors corresponding to the frame locals
+ */
+ public static List<PropertyReference> computeLocals(FrameObject frame) {
+ List<PropertyObject> args = frame.getArguments();
+ List<PropertyObject> locals = frame.getLocals();
+
+ int maxLookups = args.size() + locals.size() + 1 /* "this" */;
+
+ List<PropertyReference> localRefs = new ArrayList<PropertyReference>(maxLookups);
+
+ {
+ // Receiver ("this")
+ RefWithDisplayData receiverObject = frame.getReceiver().asWithDisplayData();
+ V8ProtocolUtil.putMirror(localRefs, receiverObject,
+ V8ProtocolUtil.PropertyNameGetter.THIS);
+ }
+
+ // Arguments
+ for (int i = 0; i < args.size(); i++) {
+ PropertyObject arg = args.get(i);
+ V8ProtocolUtil.putMirror(localRefs, arg, V8ProtocolUtil.PropertyNameGetter.SUBPROPERTY);
+ }
+
+ // Locals
+ for (int i = 0; i < locals.size(); i++) {
+ PropertyObject local = locals.get(i);
+ V8ProtocolUtil.putMirror(localRefs, local, V8ProtocolUtil.PropertyNameGetter.SUBPROPERTY);
+ }
+
+ return localRefs;
+ }
+
+ public static List<ScopeMirror> computeScopes(FrameObject frame) {
+ List<ScopeRef> scopes = frame.getScopes();
+
+ final List<ScopeMirror> result = new ArrayList<ScopeMirror>(scopes.size());
+
+ for (int i = 0; i < scopes.size(); i++) {
+ ScopeRef scope = scopes.get(i);
+ int type = (int) scope.type();
+ int index = (int) scope.index();
+
+ result.add(new ScopeMirror(type, index));
+ }
+
+ return result;
+ }
+
+ public static PropertyReference computeReceiverRef(FrameObject frame) {
+ RefWithDisplayData receiverObject = frame.getReceiver().asWithDisplayData();
+ return V8ProtocolUtil.extractProperty(receiverObject,
+ V8ProtocolUtil.PropertyNameGetter.THIS);
+ }
+
+ /**
+ * Constructs a ValueMirror given a V8 debugger object specification.
+ *
+ * @param jsonValue containing the object specification from the V8 debugger
+ * @return a {@link PropertyHoldingValueMirror} instance, containing data
+ * from jsonValue; not null
+ */
+ public static PropertyHoldingValueMirror createMirrorFromLookup(ValueHandle valueHandle) {
+ String text = valueHandle.text();
+ if (text == null) {
+ throw new ValueLoadException("Bad lookup result");
+ }
+ String typeString = valueHandle.type();
+ String className = valueHandle.className();
+ Type type = JsDataTypeUtil.fromJsonTypeAndClassName(typeString, className);
+ if (type == null) {
+ throw new ValueLoadException("Bad lookup result: type field not recognized: " + typeString);
+ }
+ return createMirrorFromLookup(valueHandle, type);
+ }
+
+ /**
+ * Constructs a {@link ValueMirror} given a V8 debugger object specification if it's possible.
+ * @return a {@link ValueMirror} instance, containing data
+ * from {@code jsonValue}; or {@code null} if {@code jsonValue} is not a handle
+ */
+ public static ValueMirror createValueMirrorOptional(DataWithRef handleFromProperty) {
+ RefWithDisplayData withData = handleFromProperty.getWithDisplayData();
+ if (withData == null) {
+ return null;
+ }
+ return createValueMirror(withData);
+ }
+ public static ValueMirror createValueMirrorOptional(ValueHandle valueHandle) {
+ return createValueMirror(valueHandle);
+ }
+
+ private static ValueMirror createValueMirror(ValueHandle valueHandle) {
+ String className = valueHandle.className();
+ Type type = JsDataTypeUtil.fromJsonTypeAndClassName(valueHandle.type(), className);
+ if (type == null) {
+ throw new ValueLoadException("Bad value object");
+ }
+ String text = valueHandle.text();
+ return createMirrorFromLookup(valueHandle, type).getValueMirror();
+ }
+
+ private static ValueMirror createValueMirror(RefWithDisplayData jsonValue) {
+ String className = jsonValue.className();
+ Type type = JsDataTypeUtil.fromJsonTypeAndClassName(jsonValue.type(), className);
+ if (type == null) {
+ throw new ValueLoadException("Bad value object");
+ }
+ { // try another format
+ if (Type.isObjectType(type)) {
+ int refId = (int) jsonValue.ref();
+ return ValueMirror.createObjectUnknownProperties(refId, type, className);
+ } else {
+ // try another format
+ Object valueObj = jsonValue.value();
+ String valueStr;
+ if (valueObj == null) {
+ valueStr = jsonValue.type(); // e.g. "undefined"
+ } else {
+ valueStr = valueObj.toString();
+ }
+ return ValueMirror.createScalar(valueStr, type, className).getValueMirror();
+ }
+ }
+ }
+
+ private static PropertyHoldingValueMirror createMirrorFromLookup(ValueHandle valueHandle,
+ Type type) {
+ if (Type.isObjectType(type)) {
+ ObjectValueHandle objectValueHandle = valueHandle.asObject();
+ int refId = (int) valueHandle.handle();
+ SubpropertiesMirror subpropertiesMirror;
+ if (type == Type.TYPE_FUNCTION) {
+ FunctionValueHandle functionValueHandle = objectValueHandle.asFunction();
+ subpropertiesMirror = new SubpropertiesMirror.FunctionValueBased(functionValueHandle,
+ FUNCTION_PROPERTY_FACTORY2);
+ } else {
+ subpropertiesMirror =
+ new SubpropertiesMirror.ObjectValueBased(objectValueHandle, null);
+ }
+ return ValueMirror.createObject(refId, subpropertiesMirror, type, valueHandle.className());
+ } else {
+ return ValueMirror.createScalar(valueHandle.text(), type, valueHandle.className());
+ }
+ }
+
+ // TODO(peter.rybin): Get rid of this monstrosity once we switched to type JSON interfaces.
+ private static final
+ SubpropertiesMirror.JsonBased.AdditionalPropertyFactory<FunctionValueHandle>
+ FUNCTION_PROPERTY_FACTORY2 =
+ new SubpropertiesMirror.JsonBased.AdditionalPropertyFactory<FunctionValueHandle>() {
+ public Object createAdditionalProperties(FunctionValueHandle jsonWithProperties) {
+ Long pos = jsonWithProperties.position();
+ if (pos == null) {
+ pos = Long.valueOf(FunctionAdditionalProperties.NO_POSITION);
+ }
+ Long scriptId = jsonWithProperties.scriptId();
+ if (scriptId == null) {
+ scriptId = Long.valueOf(FunctionAdditionalProperties.NO_SCRIPT_ID);
+ }
+ return new FunctionAdditionalProperties(pos.intValue(), scriptId.intValue());
+ }
+ };
+
+ public static <MESSAGE, RES, EX extends Exception> RES callV8Sync(
+ V8CommandSender<MESSAGE, EX> commandSender, MESSAGE message,
+ V8BlockingCallback<RES> callback) throws EX {
+ return callV8Sync(commandSender, message, callback,
+ CallbackSemaphore.OPERATION_TIMEOUT_MS);
+ }
+
+ public static <MESSAGE, RES, EX extends Exception> RES callV8Sync(
+ V8CommandSender<MESSAGE, EX> commandSender,
+ MESSAGE message, final V8BlockingCallback<RES> callback, long timeoutMs) throws EX {
+ CallbackSemaphore syncCallback = new CallbackSemaphore();
+ final Exception [] exBuff = { null };
+ // A long way of creating buffer for generic type without warnings.
+ final List<RES> resBuff = new ArrayList<RES>(Collections.nCopies(1, (RES)null));
+ V8CommandProcessor.V8HandlerCallback callbackWrapper =
+ new V8CommandProcessor.V8HandlerCallback() {
+ public void failure(String message) {
+ exBuff[0] = new Exception("Failure: " + message);
+ }
+
+ public void messageReceived(CommandResponse response) {
+ RES result = callback.messageReceived(response);
+ resBuff.set(0, result);
+ }
+ };
+ commandSender.sendV8CommandAsync(message, true, callbackWrapper, syncCallback);
+
+ boolean waitRes;
+ try {
+ waitRes = syncCallback.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
+ } catch (RuntimeException e) {
+ throw new CallbackException(e);
+ }
+
+ if (!waitRes) {
+ throw new CallbackException("Timeout");
+ }
+
+ if (exBuff[0] != null) {
+ throw new CallbackException(exBuff[0]);
+ }
+
+ return resBuff.get(0);
+ }
+
+ /**
+ * Special kind of exceptions for problems in receiving or waiting for the answer.
+ * Clients may try to catch it.
+ */
+ public static class CallbackException extends RuntimeException {
+ CallbackException() {
+ }
+ CallbackException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ CallbackException(String message) {
+ super(message);
+ }
+ CallbackException(Throwable cause) {
+ super(cause);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8Protocol.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,202 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8;
+
+/**
+ * Events, command and response types and keys for the V8 Debugger
+ * protocol messages.
+ */
+public enum V8Protocol implements CharSequence {
+
+ KEY_SEQ("seq"),
+
+ KEY_REQSEQ("request_seq"),
+
+ KEY_TYPE("type"),
+
+ TYPE_EVENT("event"),
+
+ /**
+ * VM state.
+ */
+ KEY_RUNNING("running"),
+
+ TYPE_RESPONSE("response"),
+
+ TYPE_REQUEST("request"),
+
+ COMMAND_CONTINUE("continue"),
+
+ COMMAND_EVALUATE("evaluate"),
+
+ COMMAND_BACKTRACE("backtrace"),
+
+ COMMAND_FRAME("frame"),
+
+ COMMAND_SCRIPTS("scripts"),
+
+ COMMAND_SOURCE("source"),
+
+ COMMAND_SETBP("setbreakpoint"),
+
+ KEY_COMMAND("command"),
+
+ KEY_SUCCESS("success"),
+
+ KEY_MESSAGE("message"),
+
+ KEY_BODY("body"),
+
+ KEY_V8_VERSION("V8Version"),
+
+ FRAME_RECEIVER("receiver"),
+
+ FRAME_FUNC("func"),
+
+ BODY_INDEX("index"),
+
+ BODY_LOCALS("locals"),
+
+ BODY_SCOPES("scopes"),
+
+ BODY_ARGUMENTS("arguments"),
+
+ FRAME_SCRIPT("script"),
+
+ ARGUMENT_NAME("name"),
+
+ ARGUMENT_VALUE("value"),
+
+ LOCAL_NAME("name"),
+
+ LOCAL_VALUE("value"),
+
+ FRAME_REFS("refs"),
+
+ INFERRED_NAME("inferredName"),
+
+ /**
+ * Ref handle object. It contains name, value, type, handle (ref #).
+ */
+ REF_HANDLE("handle"),
+
+ REF_TEXT("text"),
+
+ REF_TYPE("type"),
+
+ /**
+ * A primitive type value.
+ */
+ REF_VALUE("value"),
+
+ /**
+ * A string-type value length.
+ */
+ REF_LENGTH("length"),
+
+ /**
+ * Object properties.
+ */
+ REF_PROPERTIES("properties"),
+
+ REF_PROP_NAME("name"),
+
+ REF_CONSTRUCTORFUNCTION("constructorFunction"),
+
+ REF_PROTOOBJECT("protoObject"),
+
+ REF_PROTOTYPEOBJECT("prototypeObject"),
+
+ REF_CLASSNAME("className"),
+
+ REF_PROP_TYPE("propertyType"),
+
+ BODY_FRAMES("frames"),
+
+ BODY_FRAME_TEXT("text"),
+
+ BODY_FRAME_LINE("line"),
+
+ BODY_FRAME_SRCLINE("sourceLineText"),
+
+ BODY_SOURCELINE("sourceLine"),
+
+ BODY_SOURCECOLUMN("sourceColumn"),
+
+ BODY_FRAME_POSITION("position"),
+
+ BREAK_BODY("body"),
+
+ BREAK_BREAKPOINTS("breakpoints"),
+
+ KEY_EVENT("event"),
+
+ EVENT_BREAK("break"),
+
+ EVENT_EXCEPTION("exception"),
+
+ /**
+ * Scripts and Source response.
+ */
+ SOURCE_CODE("source"),
+
+ /**
+ * Script name.
+ */
+ BODY_NAME("name"),
+
+ BODY_LINEOFFSET("lineOffset"),
+
+ BODY_LINECOUNT("lineCount"),
+
+ BODY_SCRIPT_TYPE("scriptType"),
+
+ BODY_SOURCE("body"),
+
+ BODY_SETBP("body"),
+
+ BODY_BREAKPOINT("breakpoint"),
+
+ BODY_TYPE("type"),
+
+ EVAL_BODY("body"),
+
+ REF("ref"),
+
+ ID("id"),
+
+ DATA("data"),
+
+ CONTEXT("context"),
+
+ EXCEPTION("exception"),
+
+ UNCAUGHT("uncaught"),
+
+ ;
+
+ public final String key;
+
+ private V8Protocol(String key) {
+ this.key = key;
+ }
+
+ public char charAt(int index) {
+ return key.charAt(index);
+ }
+
+ public int length() {
+ return key.length();
+ }
+
+ public CharSequence subSequence(int start, int end) {
+ return key.subSequence(start, end);
+ }
+
+ @Override
+ public String toString() {
+ return key;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,386 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.chromium.sdk.Script;
+import org.chromium.sdk.Version;
+import org.chromium.sdk.Script.Type;
+import org.chromium.sdk.internal.DataWithRef;
+import org.chromium.sdk.internal.JsonUtil;
+import org.chromium.sdk.internal.PropertyReference;
+import org.chromium.sdk.internal.PropertyType;
+import org.chromium.sdk.internal.V8ContextFilter;
+import org.chromium.sdk.internal.protocol.AfterCompileBody;
+import org.chromium.sdk.internal.protocol.BacktraceCommandBody;
+import org.chromium.sdk.internal.protocol.BreakEventBody;
+import org.chromium.sdk.internal.protocol.BreakpointBody;
+import org.chromium.sdk.internal.protocol.CommandResponse;
+import org.chromium.sdk.internal.protocol.CommandResponseBody;
+import org.chromium.sdk.internal.protocol.EventNotification;
+import org.chromium.sdk.internal.protocol.EventNotificationBody;
+import org.chromium.sdk.internal.protocol.FailedCommandResponse;
+import org.chromium.sdk.internal.protocol.FrameObject;
+import org.chromium.sdk.internal.protocol.IncomingMessage;
+import org.chromium.sdk.internal.protocol.ScopeBody;
+import org.chromium.sdk.internal.protocol.ScopeRef;
+import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
+import org.chromium.sdk.internal.protocol.VersionBody;
+import org.chromium.sdk.internal.protocol.data.ContextData;
+import org.chromium.sdk.internal.protocol.data.ContextHandle;
+import org.chromium.sdk.internal.protocol.data.FunctionValueHandle;
+import org.chromium.sdk.internal.protocol.data.ObjectValueHandle;
+import org.chromium.sdk.internal.protocol.data.PropertyObject;
+import org.chromium.sdk.internal.protocol.data.PropertyWithRef;
+import org.chromium.sdk.internal.protocol.data.PropertyWithValue;
+import org.chromium.sdk.internal.protocol.data.RefWithDisplayData;
+import org.chromium.sdk.internal.protocol.data.ScriptHandle;
+import org.chromium.sdk.internal.protocol.data.SomeHandle;
+import org.chromium.sdk.internal.protocol.data.SomeRef;
+import org.chromium.sdk.internal.protocol.data.SomeSerialized;
+import org.chromium.sdk.internal.protocol.data.ValueHandle;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolModelParseException;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.protocolparser.dynamicimpl.JsonProtocolParser;
+import org.chromium.sdk.internal.tools.v8.request.ScriptsMessage;
+import org.json.simple.JSONObject;
+
+/**
+ * A utility class to process V8 debugger messages.
+ */
+public class V8ProtocolUtil {
+
+ /**
+ * Computes a script type given a V8 Long type value
+ *
+ * @param typeNumber a type designator from a V8 JSON response
+ * @return a type corresponding to {@code typeNumber} or {@code null} if
+ * {@code typeNumber == null}
+ */
+ public static Script.Type getScriptType(Long typeNumber) {
+ if (typeNumber == null) {
+ return null;
+ }
+ switch (typeNumber.intValue()) {
+ case ScriptsMessage.SCRIPTS_NORMAL:
+ return Type.NORMAL;
+ case ScriptsMessage.SCRIPTS_NATIVE:
+ return Type.NATIVE;
+ case ScriptsMessage.SCRIPTS_EXTENSION:
+ return Type.EXTENSION;
+ default:
+ throw new IllegalArgumentException("unknown script type: " + typeNumber);
+ }
+ }
+
+ /**
+ * Returns the value of "ref" field in object corresponding to the fieldName
+ * in parent.
+ *
+ * @param parent to get the object from
+ * @param fieldName of the object to get the "ref" from
+ * @return ref value or null if fieldName or "ref" not found
+ */
+ public static Long getObjectRef(SomeRef child) {
+ if (child == null) {
+ return null;
+ }
+ return child.ref();
+ }
+
+
+ /**
+ * Constructs {@code PropertyReference}s from the specified object, be it in
+ * the "original" or "inlineRefs" format.
+ *
+ * @param handle to get property references from
+ * @return an array of PropertyReferences
+ */
+ public static List<? extends PropertyReference> extractObjectProperties(
+ ObjectValueHandle handle) {
+ List<PropertyObject> props = handle.properties();
+ int propsLen = props.size();
+ List<PropertyReference> objProps = new ArrayList<PropertyReference>(propsLen);
+ for (int i = 0; i < propsLen; i++) {
+ PropertyObject prop = props.get(i);
+ putMirror(objProps, prop, PropertyNameGetter.SUBPROPERTY);
+ }
+
+ return objProps;
+ }
+ public static List<? extends PropertyReference> extractObjectInternalProperties(
+ ObjectValueHandle handle) {
+ List<PropertyReference> objProps = new ArrayList<PropertyReference>(3);
+ SomeRef protoObject = handle.protoObject();
+ RefWithDisplayData protoObjectRef = protoObject.asWithDisplayData();
+ if (protoObjectRef != null) {
+ putMirror(objProps, protoObjectRef, PropertyNameGetter.PROTO_OBJECT);
+ }
+ SomeRef constructorFunction = handle.constructorFunction();
+ RefWithDisplayData constructorFunctionRef = constructorFunction.asWithDisplayData();
+ if (constructorFunctionRef != null) {
+ putMirror(objProps, constructorFunctionRef, PropertyNameGetter.CONSTRUCTOR_FUNCTION);
+ }
+ return objProps;
+ }
+
+ static <OBJ> void putMirror(List<PropertyReference> refs, OBJ propertyObject,
+ V8ProtocolUtil.PropertyNameGetter<OBJ> nameGetter) {
+ PropertyReference propertyRef = V8ProtocolUtil.extractProperty(propertyObject, nameGetter);
+ if (propertyRef != null) {
+ refs.add(propertyRef);
+ }
+ }
+
+ /**
+ * Constructs one {@code PropertyReference} from the specified object, be it in
+ * the "original" or "inlineRefs" format.
+ *
+ * @param prop json object
+ * @param valuePropertyName name of value property in this prop object, might be null
+ * @return PropertyReference or null if we ignore this property
+ */
+ public static <OBJ> PropertyReference extractProperty(OBJ prop,
+ PropertyNameGetter<OBJ> nameGetter) {
+ String name = nameGetter.getName(prop);
+ if (name == null) {
+ return null;
+ }
+
+ if (isInternalProperty(name)) {
+ return null;
+ }
+
+ DataWithRef propValue = nameGetter.getRef(prop);
+
+ Long propType = nameGetter.getPropertyType(prop);
+ // propType is NORMAL by default
+ int propTypeValue = propType != null
+ ? propType.intValue()
+ : PropertyType.NORMAL.value;
+ if (propTypeValue == PropertyType.FIELD.value ||
+ propTypeValue == PropertyType.CONSTANT_FUNCTION.value ||
+ propTypeValue == PropertyType.CALLBACKS.value ||
+ propTypeValue == PropertyType.NORMAL.value) {
+ return new PropertyReference(name, propValue);
+ }
+ return null;
+ }
+
+ static abstract class PropertyNameGetter<OBJ> {
+ static class SimpleNameGetter extends PropertyNameGetter<RefWithDisplayData> {
+ private final String name;
+ SimpleNameGetter(String name) {
+ this.name = name;
+ }
+ @Override
+ String getName(RefWithDisplayData ref) {
+ return name;
+ }
+ @Override
+ DataWithRef getRef(RefWithDisplayData prop) {
+ return DataWithRef.fromSomeRef(prop.getSuper());
+ }
+ @Override
+ Long getPropertyType(RefWithDisplayData prop) {
+ return null;
+ }
+ }
+
+ static final PropertyNameGetter<PropertyObject> LOCAL = new SubpropertyNameGetter() {
+ @Override
+ String getName(PropertyObject ref) {
+ String name = super.getName(ref);
+ if (V8ProtocolUtil.isInternalProperty(name)) {
+ return null;
+ }
+ return name;
+ }
+ };
+ /** The name of the "this" object to report as a variable name. */
+ static final PropertyNameGetter<RefWithDisplayData> THIS = new SimpleNameGetter("this");
+ static final PropertyNameGetter<RefWithDisplayData> PROTO_OBJECT =
+ new SimpleNameGetter("__proto__");
+ static final PropertyNameGetter<RefWithDisplayData> CONSTRUCTOR_FUNCTION =
+ new SimpleNameGetter("constructor");
+
+ static final PropertyNameGetter<PropertyObject> SUBPROPERTY = new SubpropertyNameGetter();
+ static class SubpropertyNameGetter extends PropertyNameGetter<PropertyObject> {
+ @Override
+ String getName(PropertyObject ref) {
+ Object nameObject = ref.name();
+ return nameObject.toString();
+ }
+ @Override
+ DataWithRef getRef(PropertyObject prop) {
+ PropertyWithValue asPropertyWithValue = prop.asPropertyWithValue();
+ if (asPropertyWithValue != null) {
+ return DataWithRef.fromSomeRef(asPropertyWithValue.getValue().getSuper());
+ } else {
+ return DataWithRef.fromLong(prop.asPropertyWithRef().ref());
+ }
+ }
+ @Override
+ Long getPropertyType(PropertyObject prop) {
+ PropertyWithRef asPropertyWithRef = prop.asPropertyWithRef();
+ if (asPropertyWithRef == null) {
+ return null;
+ }
+ return asPropertyWithRef.propertyType();
+ }
+ }
+
+ abstract DataWithRef getRef(OBJ prop);
+
+ /**
+ * @return property name or null if we should skip this property
+ */
+ abstract String getName(OBJ ref);
+ abstract Long getPropertyType(OBJ prop);
+ }
+
+ /**
+ * @param propertyName the property name to check
+ * @return whether the given property name corresponds to an internal V8
+ * property
+ */
+ public static boolean isInternalProperty(String propertyName) {
+ // Chrome can return properties like ".arguments". They should be ignored.
+ return propertyName.length() == 0 || propertyName.startsWith(".");
+ }
+
+ /**
+ * Gets a function name from the given function handle.
+ *
+ * @param functionObject the function handle
+ * @return the actual of inferred function name. Will handle {@code null} or
+ * unnamed functions
+ */
+ public static String getFunctionName(JSONObject functionObject) {
+ if (functionObject == null) {
+ return "<unknown>";
+ } else {
+ String name = getNameOrInferred(functionObject, V8Protocol.LOCAL_NAME);
+ if (isNullOrEmpty(name)) {
+ return "(anonymous function)";
+ } else {
+ return name;
+ }
+ }
+ }
+
+ /**
+ * Gets a script id from a script response.
+ *
+ * @param scriptObject to get the "id" value from
+ * @return the script id
+ */
+ public static Long getScriptIdFromResponse(ScriptHandle scriptObject) {
+ return scriptObject.id();
+ }
+
+ /**
+ * Determines if a {@code script} is valid in the current debug context.
+ * Returns {@code null} if it is not, otherwise returns {@code script}.
+ *
+ * @param script to check and, possibly, modify
+ * @param refs from the corresponding V8 response
+ * @return script with a non-null name if the script is valid, {@code null}
+ * otherwise
+ */
+ public static ScriptHandle validScript(ScriptHandle script, List<SomeHandle> refs,
+ V8ContextFilter contextFilter) {
+ Long contextRef = V8ProtocolUtil.getObjectRef(script.context());
+ for (int i = 0, size = refs.size(); i < size; i++) {
+ SomeHandle ref = refs.get(i);
+ if (ref.handle() != contextRef.longValue()) {
+ continue;
+ }
+ ContextHandle contextHandle;
+ try {
+ contextHandle = ref.asContextHandle();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ if (!contextFilter.isContextOurs(contextHandle)) {
+ return null;
+ }
+ return script;
+ }
+ return null; // good context not found
+ }
+
+ public static Version parseVersionResponse(SuccessCommandResponse versionResponse) {
+ VersionBody body;
+ try {
+ body = versionResponse.getBody().asVersionBody();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ String versionString = body.getV8Version();
+ if (versionString == null) {
+ return null;
+ }
+ return Version.parseString(versionString);
+ }
+
+ private static String getNameOrInferred(JSONObject obj, V8Protocol nameProperty) {
+ String name = JsonUtil.getAsString(obj, nameProperty);
+ if (isNullOrEmpty(name)) {
+ name = JsonUtil.getAsString(obj, V8Protocol.INFERRED_NAME);
+ }
+ return name;
+ }
+
+ private static boolean isNullOrEmpty(String value) {
+ return value == null || value.length() == 0;
+ }
+
+ public static JsonProtocolParser getV8Parser() {
+ return parser;
+ }
+
+ private static final JsonProtocolParser parser;
+ static {
+ try {
+ // TODO(peter.rybin): change to ParserHolder.
+ parser = new JsonProtocolParser(new Class<?>[] {
+ IncomingMessage.class,
+ EventNotification.class,
+ SuccessCommandResponse.class,
+ FailedCommandResponse.class,
+ CommandResponse.class,
+ BreakEventBody.class,
+ EventNotificationBody.class,
+ CommandResponseBody.class,
+ BacktraceCommandBody.class,
+ FrameObject.class,
+ BreakpointBody.class,
+ ScopeBody.class,
+ ScopeRef.class,
+ VersionBody.class,
+ AfterCompileBody.class,
+
+ SomeHandle.class,
+ ScriptHandle.class,
+ ValueHandle.class,
+ RefWithDisplayData.class,
+ PropertyObject.class,
+ PropertyWithRef.class,
+ PropertyWithValue.class,
+ ObjectValueHandle.class,
+ FunctionValueHandle.class,
+ SomeRef.class,
+ SomeSerialized.class,
+ ContextHandle.class,
+ ContextData.class,
+ });
+ } catch (JsonProtocolModelParseException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/processor/AfterCompileProcessor.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,94 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.processor;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.chromium.sdk.Script;
+import org.chromium.sdk.internal.DebugSession;
+import org.chromium.sdk.internal.V8ContextFilter;
+import org.chromium.sdk.internal.protocol.AfterCompileBody;
+import org.chromium.sdk.internal.protocol.CommandResponse;
+import org.chromium.sdk.internal.protocol.EventNotification;
+import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
+import org.chromium.sdk.internal.protocol.data.ScriptHandle;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.v8.ChromeDevToolSessionManager;
+import org.chromium.sdk.internal.tools.v8.V8CommandProcessor;
+import org.chromium.sdk.internal.tools.v8.V8ProtocolUtil;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory;
+
+/**
+ * Listens for scripts sent in the "afterCompile" events and requests their
+ * sources.
+ */
+public class AfterCompileProcessor extends V8EventProcessor {
+
+ public AfterCompileProcessor(DebugSession debugSession) {
+ super(debugSession);
+ }
+
+ @Override
+ public void messageReceived(EventNotification eventMessage) {
+ final DebugSession debugSession = getDebugSession();
+ ScriptHandle script = getScriptToLoad(eventMessage,
+ debugSession.getScriptManager().getContextFilter());
+ if (script == null) {
+ return;
+ }
+ debugSession.sendMessageAsync(
+ DebuggerMessageFactory.scripts(
+ Collections.singletonList(V8ProtocolUtil.getScriptIdFromResponse(script)), true),
+ true,
+ new V8CommandProcessor.V8HandlerCallback(){
+ public void messageReceived(CommandResponse response) {
+ SuccessCommandResponse successResponse = response.asSuccess();
+ if (successResponse == null) {
+ return;
+ }
+ List<ScriptHandle> body;
+ try {
+ body = successResponse.getBody().asScripts();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ // body is an array of scripts
+ if (body.size() == 0) {
+ return; // The script did not arrive (bad id?)
+ }
+ Script newScript = debugSession.getScriptManager().addScript(
+ body.get(0),
+ successResponse.getRefs());
+ if (newScript != null) {
+ getDebugSession().getSessionManager().getDebugEventListener().scriptLoaded(newScript);
+ }
+ }
+
+ public void failure(String message) {
+ // The script is now missing.
+ }
+ },
+ null);
+ }
+
+ private static ScriptHandle getScriptToLoad(EventNotification eventResponse,
+ V8ContextFilter contextFilter) {
+ AfterCompileBody body;
+ try {
+ body = eventResponse.getBody().asAfterCompileBody();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ ScriptHandle script = body.getScript();
+ if (ChromeDevToolSessionManager.JAVASCRIPT_VOID.equals(script.sourceStart()) ||
+ script.context() == null ||
+ V8ProtocolUtil.getScriptType(script.scriptType()) ==
+ Script.Type.NATIVE) {
+ return null;
+ }
+ return V8ProtocolUtil.validScript(script, eventResponse.getRefs(), contextFilter);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/processor/BacktraceProcessor.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,148 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.processor;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.chromium.sdk.DebugContext;
+import org.chromium.sdk.JavascriptVm;
+import org.chromium.sdk.Script;
+import org.chromium.sdk.internal.ContextBuilder;
+import org.chromium.sdk.internal.DebugSession;
+import org.chromium.sdk.internal.FrameMirror;
+import org.chromium.sdk.internal.HandleManager;
+import org.chromium.sdk.internal.protocol.BacktraceCommandBody;
+import org.chromium.sdk.internal.protocol.CommandResponse;
+import org.chromium.sdk.internal.protocol.FrameObject;
+import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
+import org.chromium.sdk.internal.protocol.data.ScriptHandle;
+import org.chromium.sdk.internal.protocol.data.SomeHandle;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+import org.chromium.sdk.internal.tools.v8.V8ProtocolUtil;
+import org.json.simple.JSONObject;
+
+/**
+ * Handles the "backtrace" V8 command replies.
+ */
+class BacktraceProcessor implements org.chromium.sdk.internal.tools.v8.V8CommandProcessor.V8HandlerCallback {
+
+ private final ContextBuilder.ExpectingBacktraceStep step2;
+
+ BacktraceProcessor(ContextBuilder.ExpectingBacktraceStep step2) {
+ this.step2 = step2;
+ }
+
+ public void messageReceived(CommandResponse response) {
+ String commandString = response.getCommand();
+
+ DebuggerCommand command = DebuggerCommand.forString(commandString);
+ if (command != DebuggerCommand.BACKTRACE) {
+ handleWrongStacktrace();
+ }
+ SuccessCommandResponse successResponse = response.asSuccess();
+ if (successResponse == null) {
+ handleWrongStacktrace();
+ }
+
+ final DebugContext debugContext = setFrames(successResponse);
+ final DebugSession debugSession = step2.getInternalContext().getDebugSession();
+
+ JavascriptVm.ScriptsCallback afterScriptsAreLoaded = new JavascriptVm.ScriptsCallback() {
+ public void failure(String errorMessage) {
+ handleWrongStacktrace();
+ }
+
+ public void success(Collection<Script> scripts) {
+ debugSession.getDebugEventListener().suspended(debugContext);
+ }
+ };
+
+ debugSession.getScriptLoader().loadAllScripts(afterScriptsAreLoaded, null);
+ }
+
+ private DebugContext setFrames(SuccessCommandResponse response) {
+ BacktraceCommandBody body;
+ try {
+ body = response.getBody().asBacktraceCommandBody();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ List<FrameObject> jsonFrames = body.getFrames();
+ int frameCnt = jsonFrames.size();
+ FrameMirror[] frameMirrors = new FrameMirror[frameCnt];
+
+ HandleManager handleManager = step2.getInternalContext().getHandleManager();
+
+ List<SomeHandle> refs = response.getRefs();
+ handleManager.putAll(refs);
+ for (int frameIdx = 0; frameIdx < frameCnt; frameIdx++) {
+ FrameObject frameObject = jsonFrames.get(frameIdx);
+ int index = (int) frameObject.getIndex();
+ FrameObject frame = jsonFrames.get(frameIdx);
+ JSONObject func = frame.getFunc();
+
+ String text = frame.getText().replace('\r', ' ').replace('\n', ' ');
+ Matcher m = FRAME_TEXT_PATTERN.matcher(text);
+ m.matches();
+ String url = m.group(1);
+
+ int currentLine = (int) frame.getLine();
+
+ // If we stopped because of the debuggerword then we're on the next
+ // line.
+ // TODO(apavlov): Terry says: we need to use the [e.g. Rhino] AST to
+ // decide if line is debuggerword. If so, find the next sequential line.
+ // The below works for simple scripts but doesn't take into account
+ // comments, etc.
+ String srcLine = frame.getSourceLineText();
+ if (srcLine.trim().startsWith(DEBUGGER_RESERVED)) {
+ currentLine++;
+ }
+ Long scriptRef = V8ProtocolUtil.getObjectRef(frame.getScript());
+
+ Long scriptId = -1L;
+ if (scriptRef != null) {
+ SomeHandle handle = handleManager.getHandle(scriptRef);
+ ScriptHandle scriptHandle;
+ try {
+ scriptHandle = handle.asScriptHandle();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ if (handle != null) {
+ scriptId = scriptHandle.id();
+ }
+ }
+ frameMirrors[index] =
+ new FrameMirror(frameObject, url, currentLine, scriptId,
+ V8ProtocolUtil.getFunctionName(func));
+ }
+
+
+ return step2.setFrames(frameMirrors);
+ }
+
+ public void failure(String message) {
+ handleWrongStacktrace();
+ }
+
+ private void handleWrongStacktrace() {
+ step2.getInternalContext().getContextBuilder().buildSequenceFailure();
+ }
+
+ private static final String DEBUGGER_RESERVED = "debugger";
+
+ /** Regex for the "text" field of the "backtrace" element response. */
+ private static final String FRAME_TEXT_REGEX =
+ "^(?:.+) ([^\\s]+) line (.+) column (.+)" + " (?:\\(position (.+)\\))?";
+
+ /** A pattern for the frame "text" regex. */
+ private static final Pattern FRAME_TEXT_PATTERN = Pattern.compile(FRAME_TEXT_REGEX);
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/processor/BreakpointProcessor.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,131 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.processor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.ExceptionData;
+import org.chromium.sdk.internal.ContextBuilder;
+import org.chromium.sdk.internal.DebugSession;
+import org.chromium.sdk.internal.ExceptionDataImpl;
+import org.chromium.sdk.internal.InternalContext;
+import org.chromium.sdk.internal.InternalContext.ContextDismissedCheckedException;
+import org.chromium.sdk.internal.protocol.BreakEventBody;
+import org.chromium.sdk.internal.protocol.EventNotification;
+import org.chromium.sdk.internal.protocol.data.SomeHandle;
+import org.chromium.sdk.internal.protocol.data.ValueHandle;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.v8.BreakpointManager;
+import org.chromium.sdk.internal.tools.v8.V8Helper;
+import org.chromium.sdk.internal.tools.v8.V8Protocol;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory;
+
+/**
+ * Handles the suspension-related V8 command replies and events.
+ */
+public class BreakpointProcessor extends V8EventProcessor {
+
+ /** The name of the "exception" object to report as a variable name. */
+ private static final String EXCEPTION_NAME = "exception";
+
+ public BreakpointProcessor(DebugSession debugSession) {
+ super(debugSession);
+ }
+
+ @Override
+ public void messageReceived(EventNotification eventMessage) {
+ final boolean isEvent = true;
+ if (isEvent) {
+ String event = eventMessage.getEvent();
+ DebugSession debugSession = getDebugSession();
+
+ ContextBuilder contextBuilder = debugSession.getContextBuilder();
+
+ ContextBuilder.ExpectingBreakEventStep step1 = contextBuilder.buildNewContext();
+
+ InternalContext internalContext = step1.getInternalContext();
+
+ BreakEventBody breakEventBody;
+ try {
+ breakEventBody = eventMessage.getBody().asBreakEventBody();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+
+ ContextBuilder.ExpectingBacktraceStep step2;
+ if (V8Protocol.EVENT_BREAK.key.equals(event)) {
+ Collection<Breakpoint> breakpointsHit = getBreakpointsHit(eventMessage, breakEventBody);
+ step2 = step1.setContextState(breakpointsHit, null);
+ } else if (V8Protocol.EVENT_EXCEPTION.key.equals(event)) {
+ ExceptionData exception = createException(eventMessage, breakEventBody,
+ internalContext);
+ step2 = step1.setContextState(Collections.<Breakpoint> emptySet(), exception);
+ } else {
+ contextBuilder.buildSequenceFailure();
+ throw new RuntimeException();
+ }
+
+ processNextStep(step2);
+ }
+ }
+
+ public void processNextStep(ContextBuilder.ExpectingBacktraceStep step2) {
+ BacktraceProcessor backtraceProcessor = new BacktraceProcessor(step2);
+ InternalContext internalContext = step2.getInternalContext();
+
+ DebuggerMessage message = DebuggerMessageFactory.backtrace(null, null, true);
+ try {
+ // Command is not immediate because we are supposed to be suspended.
+ internalContext.sendV8CommandAsync(message, false, backtraceProcessor, null);
+ } catch (ContextDismissedCheckedException e) {
+ // Can't happen -- we are just creating context, it couldn't have become invalid
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Collection<Breakpoint> getBreakpointsHit(EventNotification response,
+ BreakEventBody breakEventBody) {
+ List<Long> breakpointIdsArray = breakEventBody.getBreakpoints();
+ BreakpointManager breakpointManager = getDebugSession().getBreakpointManager();
+ if (breakpointIdsArray == null) {
+ // Suspended on step end.
+ return Collections.<Breakpoint> emptySet();
+ }
+ Collection<Breakpoint> breakpointsHit = new ArrayList<Breakpoint>(breakpointIdsArray.size());
+ for (int i = 0, size = breakpointIdsArray.size(); i < size; ++i) {
+ Breakpoint existingBp = breakpointManager.getBreakpoint(breakpointIdsArray.get(i));
+ if (existingBp != null) {
+ breakpointsHit.add(existingBp);
+ }
+ }
+ return breakpointsHit;
+ }
+
+ private ExceptionData createException(EventNotification response, BreakEventBody body,
+ InternalContext internalContext) {
+ List<SomeHandle> refs = response.getRefs();
+ ValueHandle exception = body.getException();
+ List<SomeHandle> handles = new ArrayList<SomeHandle>(refs.size() + 1);
+ handles.addAll(refs);
+ handles.add(exception.getSuper());
+ internalContext.getHandleManager().putAll(handles);
+
+ // source column is not exposed ("sourceColumn" in "body")
+ String sourceText = body.getSourceLineText();
+
+ return new ExceptionDataImpl(internalContext,
+ V8Helper.createMirrorFromLookup(exception).getValueMirror(),
+ EXCEPTION_NAME,
+ body.isUncaught(),
+ sourceText,
+ exception.text());
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/processor/V8EventProcessor.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,30 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.processor;
+
+import org.chromium.sdk.internal.DebugSession;
+import org.chromium.sdk.internal.protocol.EventNotification;
+
+/**
+ * An abstract base implementation of DebugContextImpl-aware
+ * reply handlers for certain V8 commands.
+ * <p>
+ * NB! The {@link #messageReceived(org.json.simple.JSONObject)} implementation
+ * MUST NOT perform debugger commands in a blocking way the current thread.
+ */
+public abstract class V8EventProcessor {
+
+ private final DebugSession debugSession;
+
+ public V8EventProcessor(DebugSession debugSession) {
+ this.debugSession = debugSession;
+ }
+
+ public abstract void messageReceived(EventNotification eventMessage);
+
+ protected DebugSession getDebugSession() {
+ return debugSession;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/BacktraceMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents a "backtrace" V8 request message.
+ */
+public class BacktraceMessage extends DebuggerMessage {
+
+ /**
+ * @param fromFrame nullable frame range start (0 by default)
+ * @param toFrame nullable frame range end (last frame by default)
+ * @param inlineRefs whether to inline object refs
+ */
+ public BacktraceMessage(Integer fromFrame, Integer toFrame, boolean inlineRefs) {
+ super(DebuggerCommand.BACKTRACE.value);
+ putArgument("fromFrame", fromFrame);
+ putArgument("toFrame", toFrame);
+ if (inlineRefs) {
+ putArgument("inlineRefs", inlineRefs);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/ChangeBreakpointMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents a "changeBreakpoint" V8 request message.
+ */
+public class ChangeBreakpointMessage extends ContextlessDebuggerMessage {
+
+ /**
+ * @param breakpoint id in V8
+ * @param enabled nullable initial enabled state. Default is true
+ * @param condition nullable string with break point condition
+ * @param ignoreCount nullable number specifying the number of break point hits to ignore.
+ * Default is 0
+ */
+ public ChangeBreakpointMessage(Long breakpoint, Boolean enabled,
+ String condition, Integer ignoreCount) {
+ super(DebuggerCommand.CHANGEBREAKPOINT.value);
+ putArgument("breakpoint", breakpoint);
+ putArgument("enabled", enabled);
+ putArgument("condition", condition);
+ putArgument("ignoreCount", ignoreCount);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/ClearBreakpointMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,21 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents a "clearBreakpoint" V8 request message.
+ */
+public class ClearBreakpointMessage extends ContextlessDebuggerMessage {
+
+ /**
+ * @param breakpoint id in V8 to clear
+ */
+ public ClearBreakpointMessage(Long breakpoint) {
+ super(DebuggerCommand.CLEARBREAKPOINT.value);
+ putArgument("breakpoint", breakpoint);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/ContextlessDebuggerMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,8 @@
+package org.chromium.sdk.internal.tools.v8.request;
+
+
+public class ContextlessDebuggerMessage extends DebuggerMessage {
+ public ContextlessDebuggerMessage(String command) {
+ super(command);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/ContinueMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,41 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+import org.chromium.sdk.DebugContext.StepAction;
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents a "continue" V8 request message.
+ */
+public class ContinueMessage extends DebuggerMessage {
+
+ private static final Map<StepAction, String> stepActionToV8 =
+ new EnumMap<StepAction, String>(StepAction.class);
+
+ static {
+ stepActionToV8.put(StepAction.IN, "in");
+ stepActionToV8.put(StepAction.OUT, "out");
+ stepActionToV8.put(StepAction.OVER, "next");
+ stepActionToV8.put(StepAction.CONTINUE, null);
+ }
+
+ /**
+ * @param stepAction the kind of step to perform
+ * @param stepCount nullable number of steps to perform (positive if not null).
+ * Default is 1 step. Not used when {@code stepAction == CONTINUE}
+ */
+ public ContinueMessage(StepAction stepAction, Integer stepCount) {
+ super(DebuggerCommand.CONTINUE.value);
+ String stepActionString = stepActionToV8.get(stepAction);
+ if (stepActionString != null) {
+ putArgument("stepaction", stepActionString);
+ putArgument("stepcount", stepCount);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/DebuggerMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,76 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.json.simple.JSONStreamAware;
+import org.json.simple.JSONValue;
+
+/**
+ * Represents a generic JSONStreamAware V8 request message (so that it can
+ * serialize itself into JSON.)
+ */
+public class DebuggerMessage implements JSONStreamAware {
+
+ private final int sequence;
+
+ private final String command;
+
+ private final Map<String, Object> arguments = new HashMap<String, Object>();
+
+
+ public DebuggerMessage(String command) {
+ this.sequence = SeqGenerator.getInstance().next();
+ this.command = command;
+ }
+
+ public Integer getSeq() {
+ return sequence;
+ }
+
+ public String getType() {
+ return V8MessageType.REQUEST.value;
+ }
+
+ public String getCommand() {
+ return command;
+ }
+
+ public Map<String, Object> getArguments() {
+ return arguments;
+ }
+
+ protected final void putArgument(String key, Object object) {
+ if (object != null) {
+ arguments.put(key, object);
+ }
+ }
+
+ private final void putArgumentString(String key, Object object) {
+ arguments.put(key, object.toString());
+ }
+
+ protected final void putArgumentStringIfNotNull(String key, Object object) {
+ if (object != null) {
+ putArgumentString(key, object);
+ }
+ }
+
+ public void writeJSONString(Writer out) throws IOException {
+ LinkedHashMap<String, Object> obj = new LinkedHashMap<String, Object>();
+ obj.put("seq", sequence);
+ obj.put("type", V8MessageType.REQUEST.value);
+ obj.put("command", command);
+ if (!arguments.isEmpty()) {
+ obj.put("arguments", arguments);
+ }
+ JSONValue.writeJSONString(obj, out);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/DebuggerMessageFactory.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,81 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import java.util.List;
+
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.DebugContext.StepAction;
+
+/**
+ * A factory for {@link DebuggerMessage}s. Static methods are used to construct
+ * commands to be sent to the remote V8 debugger.
+ */
+public class DebuggerMessageFactory {
+
+ public static DebuggerMessage backtrace(Integer fromFrame, Integer toFrame,
+ boolean compactFormat) {
+ return new BacktraceMessage(fromFrame, toFrame, compactFormat);
+ }
+
+ public static DebuggerMessage goOn(StepAction stepAction, Integer stepCount) {
+ return new ContinueMessage(stepAction, stepCount);
+ }
+
+ public static DebuggerMessage evaluate(String expression, Integer frame, Boolean global,
+ Boolean disableBreak) {
+ return new EvaluateMessage(expression, frame, global, disableBreak);
+ }
+
+ public static DebuggerMessage frame(Integer frameNumber) {
+ return new FrameMessage(frameNumber);
+ }
+
+ public static ContextlessDebuggerMessage scripts(Integer types, Boolean includeScripts) {
+ return new ScriptsMessage(types, includeScripts);
+ }
+
+ public static ContextlessDebuggerMessage scripts(List<Long> ids, Boolean includeScripts) {
+ return new ScriptsMessage(ids, includeScripts);
+ }
+
+ public static ContextlessDebuggerMessage source(Integer frame, Integer fromLine, Integer toLine) {
+ return new SourceMessage(frame, fromLine, toLine);
+ }
+
+ public static ContextlessDebuggerMessage setBreakpoint(Breakpoint.Type type, String target,
+ Integer line, Integer position, Boolean enabled, String condition, Integer ignoreCount) {
+ return new SetBreakpointMessage(type, target, line, position, enabled, condition, ignoreCount);
+ }
+
+ public static ContextlessDebuggerMessage changeBreakpoint(Breakpoint breakpoint) {
+ return new ChangeBreakpointMessage(breakpoint.getId(), breakpoint.isEnabled(),
+ breakpoint.getCondition(), getV8IgnoreCount(breakpoint.getIgnoreCount()));
+ }
+
+ public static ContextlessDebuggerMessage clearBreakpoint(Breakpoint breakpoint) {
+ return new ClearBreakpointMessage(breakpoint.getId());
+ }
+
+ public static DebuggerMessage lookup(List<Long> refs, Boolean inlineRefs) {
+ return new LookupMessage(refs, inlineRefs);
+ }
+
+ public static ContextlessDebuggerMessage suspend() {
+ return new SuspendMessage();
+ }
+
+ public static DebuggerMessage scope(int scopeNumber, int frameNumber) {
+ return new ScopeMessage(scopeNumber, frameNumber);
+ }
+
+ public static ContextlessDebuggerMessage version() {
+ return new VersionMessage();
+ }
+
+ private static Integer getV8IgnoreCount(int count) {
+ return count == Breakpoint.EMPTY_VALUE ? null : count;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/EvaluateMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,29 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents an "evaluate" V8 request message.
+ */
+public class EvaluateMessage extends DebuggerMessage {
+
+ /**
+ * @param expression to evaluate
+ * @param frame number (top is 0).
+ * @param global nullable. Default is false
+ * @param disableBreak nullable. Default is true
+ */
+ public EvaluateMessage(String expression, Integer frame,
+ Boolean global, Boolean disableBreak) {
+ super(DebuggerCommand.EVALUATE.value);
+ putArgument("expression", expression);
+ putArgument("frame", frame);
+ putArgument("global", global);
+ putArgument("disable_break", disableBreak);
+ putArgument("inlineRefs", Boolean.TRUE);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/FrameMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,21 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents a "frame" V8 request message.
+ */
+public class FrameMessage extends DebuggerMessage {
+
+ /**
+ * @param frame number (top is 0)
+ */
+ public FrameMessage(Integer frame) {
+ super(DebuggerCommand.FRAME.value);
+ putArgument("number", frame);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/LookupMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,22 @@
+package org.chromium.sdk.internal.tools.v8.request;
+
+import java.util.List;
+
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents a "lookup" request message.
+ */
+public class LookupMessage extends DebuggerMessage {
+
+ /**
+ * @param handles to look up
+ * @param inlineRefs whether to inline references
+ */
+ public LookupMessage(List<Long> handles, Boolean inlineRefs) {
+ super(DebuggerCommand.LOOKUP.value);
+ putArgument("handles", handles);
+ putArgument("inlineRefs", inlineRefs);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/ScopeMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,21 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents a "scope" request message.
+ */
+public class ScopeMessage extends DebuggerMessage {
+
+ public ScopeMessage(int scopeNumber, int frameNumber) {
+ super(DebuggerCommand.SCOPE.value);
+ putArgument("number", scopeNumber);
+ putArgument("frameNumber", frameNumber);
+ putArgument("inlineRefs", true);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/ScriptsMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,52 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import java.util.List;
+
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents a "scripts" V8 request message.
+ */
+public class ScriptsMessage extends ContextlessDebuggerMessage {
+
+ /**
+ * Native scripts constant.
+ */
+ public static final int SCRIPTS_NATIVE = 1 << 0;
+
+ /**
+ * Extension scripts constant.
+ */
+ public static final int SCRIPTS_EXTENSION = 1 << 1;
+
+ /**
+ * Normal scripts constant.
+ */
+ public static final int SCRIPTS_NORMAL = 1 << 2;
+
+ /**
+ * @param types a bitwise OR of script types to retrieve
+ * @param includeSource whether to include script source in the response,
+ * default is false
+ */
+ public ScriptsMessage(Integer types, Boolean includeSource) {
+ super(DebuggerCommand.SCRIPTS.value);
+ putArgument("types", types);
+ putArgument("includeSource", includeSource);
+ }
+
+ /**
+ * @param ids of scripts to retrieve
+ * @param includeSource whether to include script source in the response,
+ * default is false
+ */
+ public ScriptsMessage(List<Long> ids, Boolean includeSource) {
+ super(DebuggerCommand.SCRIPTS.value);
+ putArgument("ids", ids);
+ putArgument("includeSource", includeSource);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/SeqGenerator.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,28 @@
+// Copyright 2009 Google Inc. All Rights Reserved.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * A singleton that keeps track of the "seq" values for them to be unique across
+ * the plugin lifecycle.
+ */
+public class SeqGenerator {
+
+ private static SeqGenerator INSTANCE = new SeqGenerator();
+
+ private final AtomicInteger count = new AtomicInteger(1);
+
+ public static SeqGenerator getInstance() {
+ return INSTANCE;
+ }
+
+ public int next() {
+ return count.getAndIncrement();
+ }
+
+ private SeqGenerator() {
+ // not instantiable outside
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/SetBreakpointMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,49 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents a "setbreakpoint" V8 request message.
+ */
+public class SetBreakpointMessage extends ContextlessDebuggerMessage {
+
+ private static final Map<Breakpoint.Type, String> typeToV8Type =
+ new HashMap<Breakpoint.Type, String>();
+
+ static {
+ typeToV8Type.put(Breakpoint.Type.FUNCTION, "function");
+ typeToV8Type.put(Breakpoint.Type.SCRIPT_NAME, "script");
+ typeToV8Type.put(Breakpoint.Type.SCRIPT_ID, "scriptId");
+ }
+
+ /**
+ * @param type ("function", "handle", or "script")
+ * @param target function expression, script identification, or handle decimal number
+ * @param line in the script or function
+ * @param position of the target start within the line
+ * @param enabled whether the breakpoint is enabled initially. Nullable, default is true
+ * @param condition nullable string with breakpoint condition
+ * @param ignoreCount nullable number specifying the amount of break point hits to ignore.
+ * Default is 0
+ */
+ public SetBreakpointMessage(Breakpoint.Type type, String target,
+ Integer line, Integer position, Boolean enabled, String condition,
+ Integer ignoreCount) {
+ super(DebuggerCommand.SETBREAKPOINT.value);
+ putArgument("type", typeToV8Type.get(type));
+ putArgument("target", target);
+ putArgument("line", line);
+ putArgument("position", position);
+ putArgument("enabled", enabled);
+ putArgument("condition", condition);
+ putArgument("ignoreCount", ignoreCount);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/SourceMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,26 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents a "source" V8 request message.
+ */
+public class SourceMessage extends ContextlessDebuggerMessage {
+
+ /**
+ * @param frame number. Nullable, default is the selected frame
+ * @param fromLine nullable start line within the source. Default is line 0
+ * @param toLine nullable end line within the source (this line is not included in the
+ * result). Default is the number of lines in the script
+ */
+ public SourceMessage(Integer frame, Integer fromLine, Integer toLine) {
+ super(DebuggerCommand.SOURCE.value);
+ putArgument("frame", frame);
+ putArgument("fromLine", fromLine);
+ putArgument("toLine", toLine);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/SuspendMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,16 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents a "suspend" V8 request message.
+ */
+class SuspendMessage extends ContextlessDebuggerMessage {
+ SuspendMessage() {
+ super(DebuggerCommand.SUSPEND.value);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/V8MessageType.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,40 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Known V8 debugger protocol message types.
+ */
+public enum V8MessageType {
+
+ REQUEST("request"),
+ RESPONSE("response"),
+ EVENT("event"),
+ ;
+
+ private static final Map<String, V8MessageType> map = new HashMap<String, V8MessageType>();
+
+ static {
+ for (V8MessageType type : values()) {
+ map.put(type.value, type);
+ }
+ }
+
+ public final String value;
+
+ private V8MessageType(String value) {
+ this.value = value;
+ }
+
+ public static V8MessageType forString(String value) {
+ if (value == null) {
+ return null;
+ }
+ return map.get(value);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/VersionMessage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,17 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8.request;
+
+import org.chromium.sdk.internal.tools.v8.DebuggerCommand;
+
+/**
+ * Represents a "version" V8 request message.
+ */
+public class VersionMessage extends ContextlessDebuggerMessage {
+
+ public VersionMessage() {
+ super(DebuggerCommand.VERSION.value);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/transport/Connection.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,80 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.transport;
+
+import java.io.IOException;
+
+/**
+ * An interface to be implemented by an agent performing the communications with
+ * the debugged browser instance.
+ */
+public interface Connection {
+
+ /**
+ * An interface to be used for notification of messages coming in from the
+ * browser.
+ */
+ public interface NetListener {
+
+ /**
+ * Gets invoked whenever a message from the browser arrives.
+ * Invoked from DispatchThread.
+ * @param message from the browser instance the connection is associated
+ * with
+ */
+ void messageReceived(Message message);
+
+ /**
+ * Gets invoked from DispatchThread whenever EOS message arrives. This method
+ * must not be called more than once. Method {@link #messageReceived} must
+ * not be called after it.
+ */
+ void eosReceived();
+
+ /**
+ * Gets invoked when the physical connection has been terminated.
+ * Called from whatever thread that connection is terminated from.
+ */
+ void connectionClosed();
+ }
+
+ /**
+ * Sets a listener that will be notified of network events. The listener must
+ * be set before calling {@link #start()} and cannot be changed over the
+ * connection lifetime.
+ *
+ * @param netListener to set
+ */
+ void setNetListener(NetListener netListener);
+
+ /**
+ * Sends the specified message to the associated browser instance.
+ *
+ * @param message to send
+ */
+ void send(Message message);
+
+ /**
+ * Starts up the transport and acquire all needed resources. Does nothing if
+ * the connection has already been started.
+ *
+ * @throws IOException
+ */
+ void start() throws IOException;
+
+ /**
+ * Shuts down the transport freeing all acquired resources. Does nothing if
+ * the connection has already been shut down.
+ */
+ void close();
+
+ /**
+ * Determines the connection state.
+ *
+ * @return whether start() has been successfully invoked and close() has not
+ * been invoked yet
+ */
+ boolean isConnected();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/transport/Handshaker.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,142 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.transport;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.RunnableFuture;
+
+import org.chromium.sdk.internal.transport.Message.MalformedMessageException;
+
+/**
+ * Handshaker handles "handshake" part of communication. It may write and read whatever it needs
+ * before regular message-based communication has started.
+ */
+public interface Handshaker {
+ /**
+ * Performs handshake. This method is blocking. After it has successfully finished, input/output
+ * should be ready for normal message-based communication. In case method fails with IOException,
+ * input and output are returned in undefined state.
+ * @throws IOException if handshake process failed physically (input or output has unexpectedly
+ * closed) or logically (if unexpected message came from remote).
+ */
+ void perform(BufferedReader input, Writer output) throws IOException;
+
+ /**
+ * Implementation of handshake from Google Chrome Developer Tools Protocol. Used when we
+ * connect to browser.
+ */
+ Handshaker CHROMIUM = new Handshaker() {
+ public void perform(BufferedReader input, Writer output) throws IOException {
+ output.write(OUTGOING_MESSAGE);
+ output.flush();
+
+ // TODO(prybin): expose this as a parameter or get rid of this option if we don't need it.
+ final boolean ignoreUnexpectedResponses = false;
+
+ while (true) {
+ if (Thread.interrupted()) {
+ throw new IOException("Interrupted");
+ }
+ String line = input.readLine();
+ if (line == null) {
+ throw new IOException("Connection closed");
+ }
+ if (INCOMING_TEXT.equals(line)) {
+ break;
+ }
+ if (!ignoreUnexpectedResponses) {
+ throw new IOException("Unexpected handshake: " + line);
+ }
+ }
+ }
+
+ /**
+ * A handshake string to be sent by a browser on the connection start,
+ * specified by the protocol design doc (without trailing cr/lf).
+ */
+ private static final String INCOMING_TEXT = "ChromeDevToolsHandshake";
+
+ /**
+ * A handshake string that we send to a browser on the connection start,
+ * specified by the protocol design doc (including trailing cr/lf).
+ */
+ private static final String OUTGOING_MESSAGE = "ChromeDevToolsHandshake\r\n";
+ };
+
+ /**
+ * Stateful handshaker implementation for Standalone V8 protocol.
+ */
+ class StandaloneV8 implements Handshaker {
+ public interface RemoteInfo {
+ String getProtocolVersion();
+ String getV8VmVersion();
+ String getEmbeddingHostName();
+ }
+
+ public Future<RemoteInfo> getRemoteInfo() {
+ return runnableFuture;
+ }
+
+ private final RunnableFuture<RemoteInfo> runnableFuture =
+ new FutureTask<RemoteInfo>(new HandshakeTaks());
+
+ private BufferedReader input = null;
+
+ public void perform(BufferedReader input, Writer output) throws IOException {
+ this.input = input;
+ runnableFuture.run();
+
+ // Check for possible exceptions
+ try {
+ runnableFuture.get();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (ExecutionException e) {
+ throw new IOException("Failed to perform handshake", e);
+ }
+
+ }
+
+ class HandshakeTaks implements Callable<RemoteInfo> {
+ public RemoteInfo call() throws IOException {
+ final Message message;
+ try {
+ message = Message.fromBufferedReader(input);
+ } catch (MalformedMessageException e) {
+ throw new IOException("Unrecognized handshake message from remote", e);
+ }
+ if (message == null) {
+ throw new IOException("End of stream");
+ }
+ final String protocolVersion = message.getHeader("Protocol-Version", null);
+ if (protocolVersion == null) {
+ throw new IOException("Absent protocol version");
+ }
+ final String vmVersion = message.getHeader("V8-Version", null);
+ if (vmVersion == null) {
+ throw new IOException("Absent V8 VM version");
+ }
+ RemoteInfo remoteInfo = new RemoteInfo() {
+ public String getProtocolVersion() {
+ return protocolVersion;
+ }
+ public String getV8VmVersion() {
+ return vmVersion;
+ }
+ public String getEmbeddingHostName() {
+ return message.getHeader("Embedding-Host", null);
+ }
+ };
+ return remoteInfo;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/transport/Message.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,229 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.transport;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A transport message encapsulating the data sent/received over the wire
+ * (protocol headers and content). This class can serialize and deserialize
+ * itself into a BufferedWriter according to the ChromeDevTools Protocol
+ * specification.
+ */
+public class Message {
+
+ /**
+ * This exception is thrown during Message deserialization whenever the input
+ * is malformed.
+ */
+ public static class MalformedMessageException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public MalformedMessageException() {
+ super();
+ }
+
+ public MalformedMessageException(String message) {
+ super(message);
+ }
+
+ public MalformedMessageException(Throwable cause) {
+ super(cause);
+ }
+
+ public MalformedMessageException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ }
+
+ /**
+ * Known ChromeDevTools Protocol headers (ToolHandler implementations
+ * can add their own headers.)
+ */
+ public enum Header {
+ CONTENT_LENGTH("Content-Length"),
+ TOOL("Tool"),
+ DESTINATION("Destination"), ;
+
+ public final String name;
+
+ Header(String value) {
+ this.name = value;
+ }
+ }
+
+ /**
+ * The class logger.
+ */
+ private static final Logger LOGGER = Logger.getLogger(Message.class.getName());
+
+ /**
+ * The end of protocol header line.
+ */
+ private static final String HEADER_TERMINATOR = "\r\n";
+
+ private final HashMap<String, String> headers;
+
+ private final String content;
+
+ public Message(Map<String, String> headers, String content) {
+ this.headers = new HashMap<String, String>(headers);
+ this.content = content;
+ this.headers.put(Header.CONTENT_LENGTH.name, String.valueOf(content == null
+ ? 0
+ : content.length()));
+ }
+
+ /**
+ * Sends a message through the specified writer.
+ *
+ * @param writer to send the message through
+ * @throws IOException
+ */
+ public void sendThrough(Writer writer) throws IOException {
+ String content = maskNull(this.content);
+ for (Map.Entry<String, String> entry : this.headers.entrySet()) {
+ writeNonEmptyHeader(writer, entry.getKey(), entry.getValue());
+ }
+ writer.write(HEADER_TERMINATOR);
+ if (content.length() > 0) {
+ writer.write(content);
+ }
+ writer.flush();
+ }
+
+ /**
+ * Reads a message from the specified reader.
+ *
+ * @param reader to read message from
+ * @return a new message, or {@code null} if input is invalid (end-of-stream
+ * or bad message format)
+ * @throws IOException
+ * @throws MalformedMessageException if the input does not represent a valid
+ * message
+ */
+ public static Message fromBufferedReader(BufferedReader reader)
+ throws IOException, MalformedMessageException {
+ Map<String, String> headers = new HashMap<String, String>();
+ synchronized (reader) {
+ while (true) { // read headers
+ String line = reader.readLine();
+ if (line == null) {
+ LOGGER.fine("End of stream");
+ return null;
+ }
+ if (line.length() == 0) {
+ break; // end of headers
+ }
+ String[] nameValue = line.split(":", 2);
+ if (nameValue.length != 2) {
+ LOGGER.log(Level.SEVERE, "Bad header line: {0}", line);
+ return null;
+ } else {
+ String trimmedValue = nameValue[1].trim();
+ headers.put(nameValue[0], trimmedValue);
+ }
+ }
+
+ // Read payload if applicable
+ String contentLengthStr = getHeader(headers, Header.CONTENT_LENGTH.name, "0");
+ int contentLength = Integer.valueOf(contentLengthStr.trim());
+ char[] content = new char[contentLength];
+ int totalRead = 0;
+ LOGGER.log(Level.FINER, "Reading payload: {0} bytes", contentLength);
+ while (totalRead < contentLength) {
+ int readBytes = reader.read(content, totalRead, contentLength - totalRead);
+ if (readBytes == -1) {
+ // End-of-stream (browser closed?)
+ LOGGER.fine("End of stream while reading content");
+ return null;
+ }
+ totalRead += readBytes;
+ }
+
+ // Construct response message
+ String contentString = new String(content);
+ return new Message(headers, contentString);
+ }
+ }
+
+ /**
+ * @return the "Tool" header value
+ */
+ public String getTool() {
+ return getHeader(Header.TOOL.name, null);
+ }
+
+ /**
+ * @return the "Destination" header value
+ */
+ public String getDestination() {
+ return getHeader(Header.DESTINATION.name, null);
+ }
+
+ /**
+ * @return the message content. Never {@code null} (for no content, returns an
+ * empty String)
+ */
+ public String getContent() {
+ return content;
+ }
+
+ /**
+ * @param name of the header
+ * @param defaultValue to return if the header is not found in the message
+ * @return the {@code name} header value or {@code defaultValue} if the header
+ * is not found in the message
+ */
+ public String getHeader(String name, String defaultValue) {
+ return getHeader(this.headers, name, defaultValue);
+ }
+
+ private static String getHeader(Map<? extends String, String> headers, String headerName,
+ String defaultValue) {
+ String value = headers.get(headerName);
+ if (value == null) {
+ value = defaultValue;
+ }
+ return value;
+ }
+
+ private static String maskNull(String string) {
+ return string == null
+ ? ""
+ : string;
+ }
+
+ private static void writeNonEmptyHeader(Writer writer, String headerName, String headerValue)
+ throws IOException {
+ if (headerValue != null) {
+ writeHeader(writer, headerName, headerValue);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringWriter sw = new StringWriter();
+ try {
+ this.sendThrough(sw);
+ } catch (IOException e) {
+ // never occurs
+ }
+ return sw.toString();
+ }
+
+ private static void writeHeader(Writer writer, String name, String value) throws IOException {
+ writer.append(name).append(':').append(value).append(HEADER_TERMINATOR);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/transport/SocketConnection.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,443 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.transport;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.chromium.sdk.ConnectionLogger;
+import org.chromium.sdk.internal.transport.Message.MalformedMessageException;
+
+/**
+ * The low-level network agent handling the reading and writing of Messages
+ * using the debugger socket.
+ *
+ * This class is thread-safe.
+ */
+public class SocketConnection implements Connection {
+
+ /**
+ * A thread that can be gracefully interrupted by a third party.
+ * <p>
+ * Unfortunately there is no standard way of interrupting I/O in Java. See Bug #4514257
+ * on Java Bug Database (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4514257).
+ */
+ private static abstract class InterruptibleThread extends Thread {
+
+ protected volatile boolean isTerminated = false;
+
+ InterruptibleThread(String name) {
+ super(name);
+ }
+
+ @Override
+ public synchronized void start() {
+ this.isTerminated = false;
+ super.start();
+ }
+
+ @Override
+ public synchronized void interrupt() {
+ this.isTerminated = true;
+ super.interrupt();
+ }
+ }
+
+ /**
+ * Character encoding used in the socket data interchange.
+ */
+ private static final String SOCKET_CHARSET = "UTF-8";
+
+ /**
+ * A thread writing client-supplied messages into the debugger socket.
+ */
+ private class WriterThread extends InterruptibleThread {
+
+ private final BufferedWriter writer;
+
+ public WriterThread(BufferedWriter writer) {
+ super("WriterThread");
+ this.writer = writer;
+ }
+
+ @Override
+ public void run() {
+ while (!isTerminated && isAttached.get()) {
+ try {
+ handleOutboundMessage(outboundQueue.take());
+ } catch (InterruptedException e) {
+ // interrupt called on this thread, exit on isTerminated
+ }
+ }
+ }
+
+ private void handleOutboundMessage(Message message) {
+ try {
+ LOGGER.log(Level.FINER, "-->{0}", message);
+ message.sendThrough(writer);
+ } catch (IOException e) {
+ SocketConnection.this.shutdown(e, false);
+ }
+ }
+ }
+
+ private static abstract class MessageItem {
+ abstract void report(NetListener listener);
+ abstract boolean isEos();
+ }
+ private static final MessageItem EOS = new MessageItem() {
+ @Override
+ void report(NetListener listener) {
+ LOGGER.log(Level.FINER, "<--EOS");
+ listener.eosReceived();
+ }
+ @Override
+ boolean isEos() {
+ return true;
+ }
+ };
+ private static class RegularMessageItem extends MessageItem {
+ private final Message message;
+ RegularMessageItem(Message message) {
+ this.message = message;
+ }
+ @Override
+ void report(NetListener listener) {
+ LOGGER.log(Level.FINER, "<--{0}", message);
+ listener.messageReceived(message);
+ }
+ @Override
+ boolean isEos() {
+ return false;
+ }
+ }
+
+ /**
+ * A thread reading data from the debugger socket.
+ */
+ private class ReaderThread extends InterruptibleThread {
+
+ private final BufferedReader reader;
+ private final Writer handshakeWriter;
+
+ public ReaderThread(BufferedReader reader, Writer handshakeWriter) {
+ super("ReaderThread");
+ this.reader = reader;
+ this.handshakeWriter = handshakeWriter;
+ }
+
+ @Override
+ public void run() {
+ Exception breakException;
+ try {
+ /** The thread that dispatches the inbound messages (to avoid queue growth.) */
+ startResponseDispatcherThread();
+
+ if (connectionLogger != null) {
+ connectionLogger.start();
+ }
+
+ handshaker.perform(reader, handshakeWriter);
+
+ startWriterThread();
+
+ while (!isTerminated && isAttached.get()) {
+ Message message;
+ try {
+ message = Message.fromBufferedReader(reader);
+ } catch (MalformedMessageException e) {
+ LOGGER.log(Level.SEVERE, "Malformed protocol message", e);
+ continue;
+ }
+ if (message == null) {
+ LOGGER.fine("End of stream");
+ break;
+ }
+ inboundQueue.add(new RegularMessageItem(message));
+ }
+ breakException = null;
+ } catch (IOException e) {
+ breakException = e;
+ } finally {
+ inboundQueue.add(EOS);
+ }
+ if (!isInterrupted()) {
+ SocketConnection.this.shutdown(breakException, false);
+ }
+ }
+ }
+
+ /**
+ * A thread dispatching V8 responses (to avoid locking the ReaderThread.)
+ */
+ private class ResponseDispatcherThread extends Thread {
+
+ public ResponseDispatcherThread() {
+ super("ResponseDispatcherThread");
+ }
+
+ @Override
+ public void run() {
+ MessageItem messageItem;
+ try {
+ while (true) {
+ messageItem = inboundQueue.take();
+ try {
+ messageItem.report(listener);
+ } catch (Exception e) {
+ LOGGER.log(Level.SEVERE, "Exception in message listener", e);
+ }
+ if (messageItem.isEos()) {
+ if (connectionLogger != null) {
+ connectionLogger.handleEos();
+ }
+ break;
+ }
+ }
+ } catch (InterruptedException e) {
+ // terminate thread
+ }
+ }
+ }
+
+ /** The class logger. */
+ private static final Logger LOGGER = Logger.getLogger(SocketConnection.class.getName());
+
+ /** Lameduck shutdown delay in ms. */
+ private static final int LAMEDUCK_DELAY_MS = 1000;
+
+ /** The input stream buffer size. */
+ private static final int INPUT_BUFFER_SIZE_BYTES = 65536;
+
+ private static final NetListener NULL_LISTENER = new NetListener() {
+ public void connectionClosed() {
+ }
+
+ public void eosReceived() {
+ }
+
+ public void messageReceived(Message message) {
+ }
+ };
+
+ /** Whether the agent is currently attached to a remote browser. */
+ private AtomicBoolean isAttached = new AtomicBoolean(false);
+
+ /** The communication socket. */
+ protected Socket socket;
+
+ /** The socket reader. */
+ protected BufferedReader reader;
+
+ /** The socket writer. */
+ protected BufferedWriter writer;
+
+ private final ConnectionLogger connectionLogger;
+
+ /** Handshaker used to establish connection. */
+ private final Handshaker handshaker;
+
+ /** The listener to report network events to. */
+ protected volatile NetListener listener;
+
+ /** The inbound message queue. */
+ protected final BlockingQueue<MessageItem> inboundQueue = new LinkedBlockingQueue<MessageItem>();
+
+ /** The outbound message queue. */
+ protected final BlockingQueue<Message> outboundQueue = new LinkedBlockingQueue<Message>();
+
+ /** The socket endpoint. */
+ private final SocketAddress socketEndpoint;
+
+ /** The thread that processes the outbound queue. */
+ private WriterThread writerThread;
+
+ /** The thread that processes the inbound queue. */
+ private ReaderThread readerThread;
+
+ /** Connection attempt timeout in ms. */
+ private final int connectionTimeoutMs;
+
+ public SocketConnection(SocketAddress endpoint, int connectionTimeoutMs,
+ ConnectionLogger connectionLogger, Handshaker handshaker) {
+ this.socketEndpoint = endpoint;
+ this.connectionTimeoutMs = connectionTimeoutMs;
+ this.connectionLogger = connectionLogger;
+ this.handshaker = handshaker;
+ }
+
+ void attach() throws IOException {
+ this.socket = new Socket();
+ this.socket.connect(socketEndpoint, connectionTimeoutMs);
+ Writer streamWriter = new OutputStreamWriter(socket.getOutputStream(), SOCKET_CHARSET);
+ Reader streamReader = new InputStreamReader(socket.getInputStream(), SOCKET_CHARSET);
+
+ if (connectionLogger != null) {
+ streamWriter = connectionLogger.wrapWriter(streamWriter);
+ streamReader = connectionLogger.wrapReader(streamReader);
+ connectionLogger.setConnectionCloser(new ConnectionLogger.ConnectionCloser() {
+ public void closeConnection() {
+ close();
+ }
+ });
+ }
+
+ this.writer = new BufferedWriter(streamWriter);
+ this.reader = new BufferedReader(streamReader, INPUT_BUFFER_SIZE_BYTES);
+ isAttached.set(true);
+
+ this.readerThread = new ReaderThread(reader, writer);
+ // We do not start WriterThread until handshake is done (see ReaderThread)
+ this.writerThread = null;
+ readerThread.setDaemon(true);
+ readerThread.start();
+ }
+
+ void detach(boolean lameduckMode) {
+ shutdown(null, lameduckMode);
+ }
+
+ void sendMessage(Message message) {
+ outboundQueue.add(message);
+ }
+
+ private boolean isAttached() {
+ return isAttached.get();
+ }
+
+ /**
+ * The method is synchronized so that it does not get called
+ * from the {Reader,Writer}Thread when the underlying socket is
+ * closed in another invocation of this method.
+ */
+ private void shutdown(Exception cause, boolean lameduckMode) {
+ if (!isAttached.compareAndSet(true, false)) {
+ // already shut down
+ return;
+ }
+ LOGGER.log(Level.INFO, "Shutdown requested", cause);
+
+ if (lameduckMode) {
+ Thread terminationThread = new Thread("ServiceThreadTerminator") {
+ @Override
+ public void run() {
+ interruptServiceThreads();
+ }
+ };
+ terminationThread.setDaemon(true);
+ terminationThread.start();
+ try {
+ terminationThread.join(LAMEDUCK_DELAY_MS);
+ } catch (InterruptedException e) {
+ // fall through
+ }
+ } else {
+ interruptServiceThreads();
+ }
+
+ try {
+ socket.shutdownInput();
+ } catch (IOException e) {
+ // ignore
+ }
+ try {
+ socket.shutdownOutput();
+ } catch (IOException e) {
+ // ignore
+ }
+
+ try {
+ socket.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ listener.connectionClosed();
+ }
+
+ private void interruptServiceThreads() {
+ interruptThread(writerThread);
+ interruptThread(readerThread);
+ }
+
+ private void startWriterThread() {
+ if (writerThread != null) {
+ throw new IllegalStateException();
+ }
+ writerThread = new WriterThread(writer);
+ writerThread.setDaemon(true);
+ writerThread.start();
+ }
+
+ private ResponseDispatcherThread startResponseDispatcherThread() {
+ ResponseDispatcherThread dispatcherThread;
+ dispatcherThread = new ResponseDispatcherThread();
+ dispatcherThread.setDaemon(true);
+ dispatcherThread.start();
+ return dispatcherThread;
+ }
+
+ private void interruptThread(Thread thread) {
+ try {
+ if (thread != null) {
+ thread.interrupt();
+ }
+ } catch (SecurityException e) {
+ // ignore
+ }
+ }
+
+ public void close() {
+ if (isAttached()) {
+ detach(true);
+ }
+ }
+
+ public boolean isConnected() {
+ return isAttached();
+ }
+
+ public void send(Message message) {
+ checkAttached();
+ sendMessage(message);
+ }
+
+ public void setNetListener(NetListener netListener) {
+ if (this.listener != null && netListener != this.listener) {
+ throw new IllegalStateException("Cannot change NetListener");
+ }
+ this.listener = netListener != null
+ ? netListener
+ : NULL_LISTENER;
+ }
+
+ public void start() throws IOException {
+ try {
+ if (!isAttached()) {
+ attach();
+ }
+ } catch (IOException e) {
+ listener.connectionClosed();
+ throw e;
+ }
+ }
+
+ private void checkAttached() {
+ if (!isAttached()) {
+ throw new IllegalStateException("Connection not attached");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/.classpath Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/.project Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.symbian.tools.wrttools.debug.core</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/.settings/org.eclipse.jdt.core.prefs Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,8 @@
+#Thu Dec 17 15:43:16 EET 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/LICENSE Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 The Chromium Authors. 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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/META-INF/MANIFEST.MF Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,25 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: WRT Debugger Plug-In
+Bundle-SymbolicName: org.symbian.tools.wrttools.debug.core;singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Bundle-Activator: org.symbian.tools.wrttools.debug.internal.Activator
+Bundle-Vendor: Symbian Foundation
+Require-Bundle: org.eclipse.ui,
+ org.eclipse.core.runtime,
+ org.chromium.debug.core;bundle-version="0.1.3",
+ org.eclipse.debug.core;bundle-version="3.5.0",
+ org.eclipse.debug.ui;bundle-version="3.5.0",
+ org.chromium.sdk;bundle-version="0.1.3",
+ org.eclipse.core.expressions;bundle-version="3.4.100",
+ org.eclipse.wst.jsdt.ui;bundle-version="1.0.200",
+ org.eclipse.equinox.http.registry,
+ org.eclipse.equinox.http.jetty;bundle-version="2.0.0",
+ org.eclipse.ui.editors;bundle-version="3.5.0",
+ org.eclipse.jface.text;bundle-version="3.5.0"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-ActivationPolicy: lazy
+Import-Package: javax.servlet;version="2.5.0",
+ javax.servlet.http;version="2.5.0",
+ org.eclipse.equinox.jsp.jasper;version="1.0.0",
+ org.osgi.service.http;version="1.2.1"
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/Activator.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/BreakpointAdapterFactory.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/ChromeDebugUtils.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/IConstants.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/Images.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/PreferenceInitializer.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/WorkspaceLineBreakpointAdapter.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/launch/DebugConnectionJob$1.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/launch/DebugConnectionJob$2.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/launch/DebugConnectionJob$3.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/launch/DebugConnectionJob$4.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/launch/DebugConnectionJob.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/launch/WidgetLaunchDelegate.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/launch/WidgetTabSelector.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$1.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$2.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$3.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$4.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$5.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$6$1.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$6.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$7$1.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$7.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$8.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$DebugEventListenerImpl.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/JavascriptThread.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/ResourceManager$ScriptIdentifier.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/ResourceManager.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/StackFrame.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/property/PropertyTester.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/web/EmulatorContext.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/web/WebAppInterface.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/web/WebappManager.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/web/WorkspaceResourcesServlet.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/DebugPreferencePage.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/actions/JsBreakpointPropertiesRulerAction$1.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/actions/JsBreakpointPropertiesRulerAction.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/actions/JsBreakpointPropertiesRulerActionDelegate.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/launch/WidgetBasicTab$1.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/launch/WidgetBasicTab.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/launch/WidgetLaunchConfigurationTabGroup.class has changed
Binary file org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/launch/WidgetLaunchShortcut.class has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/build.properties Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,14 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ icons/main16.gif,\
+ http-content/,\
+ LICENSE
+src.includes = src/,\
+ .settings/,\
+ .classpath,\
+ .project,\
+ LICENSE,\
+ launch/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/http-content/wrtdebugger/connectionTest.jsp Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,9 @@
+<%@ page import="org.symbian.tools.wrttools.debug.internal.web.WebAppInterface, javax.servlet.http.HttpServletResponse" %>
+<%
+ String widget = WebAppInterface.decode(request.getParameter("widget"));
+ String id = request.getParameter("session");
+
+ if (WebAppInterface.isConnected(widget, id)) {
+ response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
+ }
+%>
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/http-content/wrtdebugger/debugger.jsp Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,33 @@
+<%@ page import="org.symbian.tools.wrttools.debug.internal.web.WebAppInterface" %>
+<%
+ String widget = WebAppInterface.decode(request.getParameter("widget"));
+ String id = request.getParameter("session");
+%>
+<html>
+<head>
+ <title><%=widget %></title>
+ <% WebAppInterface.connectDebugger(widget, id); %>
+ <script type="text/javascript">
+ var req;
+
+ function connect() {
+ req = new XMLHttpRequest();
+ req.onreadystatechange = testconnection;
+ req.open("GET", "<%=WebAppInterface.getAjaxUri(widget, id) %>", true);
+ req.send(null);
+ }
+
+ function testconnection() {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ window.setTimeout(connect, 200);
+ } else {
+ window.location = '<%=WebAppInterface.getUrl(widget, id) %>';
+ }
+ }
+ }
+ </script>
+</head>
+<body onload="connect()">
+Establishing debug connection...
+</body>
\ No newline at end of file
Binary file org.symbian.tools.wrttools.debug.core/icons/deploy_settings.gif has changed
Binary file org.symbian.tools.wrttools.debug.core/icons/deploy_widget.gif has changed
Binary file org.symbian.tools.wrttools.debug.core/icons/error_over.gif has changed
Binary file org.symbian.tools.wrttools.debug.core/icons/error_ovr.gif has changed
Binary file org.symbian.tools.wrttools.debug.core/icons/exclude_archive.gif has changed
Binary file org.symbian.tools.wrttools.debug.core/icons/icon.gif has changed
Binary file org.symbian.tools.wrttools.debug.core/icons/icon32.gif has changed
Binary file org.symbian.tools.wrttools.debug.core/icons/include_archive.gif has changed
Binary file org.symbian.tools.wrttools.debug.core/icons/main16.gif has changed
Binary file org.symbian.tools.wrttools.debug.core/icons/package_widget.gif has changed
Binary file org.symbian.tools.wrttools.debug.core/icons/upgrade-project-over.gif has changed
Binary file org.symbian.tools.wrttools.debug.core/icons/upgrade_project.gif has changed
Binary file org.symbian.tools.wrttools.debug.core/icons/upgrade_red.gif has changed
Binary file org.symbian.tools.wrttools.debug.core/icons/validate_widget.gif has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/launch/WRT Debugger.launch Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.pde.ui.RuntimeWorkbench">
+<booleanAttribute key="append.args" value="true"/>
+<booleanAttribute key="askclear" value="true"/>
+<booleanAttribute key="automaticAdd" value="true"/>
+<booleanAttribute key="automaticValidate" value="false"/>
+<stringAttribute key="bad_container_name" value="\org.symbian.tools.wrttools.debug.core\launch"/>
+<stringAttribute key="bootstrap" value=""/>
+<stringAttribute key="checked" value="[NONE]"/>
+<booleanAttribute key="clearConfig" value="false"/>
+<booleanAttribute key="clearws" value="false"/>
+<booleanAttribute key="clearwslog" value="false"/>
+<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/WRT Debugger"/>
+<booleanAttribute key="default" value="true"/>
+<booleanAttribute key="includeOptional" value="true"/>
+<stringAttribute key="location" value="${workspace_loc}/../runtime-WRT-Debugger"/>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl}"/>
+<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms128m -Xmx768m -XX:MaxPermSize=192m"/>
+<stringAttribute key="pde.version" value="3.3"/>
+<stringAttribute key="product" value="org.eclipse.sdk.ide"/>
+<booleanAttribute key="show_selected_only" value="false"/>
+<stringAttribute key="templateConfig" value="${target_home}\configuration\config.ini"/>
+<booleanAttribute key="tracing" value="false"/>
+<booleanAttribute key="useDefaultConfig" value="true"/>
+<booleanAttribute key="useDefaultConfigArea" value="true"/>
+<booleanAttribute key="useProduct" value="false"/>
+<booleanAttribute key="usefeatures" value="false"/>
+</launchConfiguration>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/plugin.xml Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+ <extension
+ point="org.eclipse.core.runtime.preferences">
+ <initializer
+ class="org.symbian.tools.wrttools.debug.internal.PreferenceInitializer">
+ </initializer>
+ </extension>
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ class="org.symbian.tools.wrttools.debug.ui.DebugPreferencePage"
+ id="org.symbian.tools.wrttools.debug"
+ name="WRT Debugger">
+ </page>
+ </extension>
+ <extension
+ point="org.eclipse.debug.core.launchConfigurationTypes">
+ <launchConfigurationType
+ delegate="org.symbian.tools.wrttools.debug.internal.launch.WidgetLaunchDelegate"
+ id="org.symbian.tools.wrttools.debug.widget"
+ modes="debug,run"
+ name="WRT Widget"
+ public="true">
+ </launchConfigurationType>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTabGroups">
+ <launchConfigurationTabGroup
+ class="org.symbian.tools.wrttools.debug.ui.launch.WidgetLaunchConfigurationTabGroup"
+ description="WRT Widget"
+ id="org.symbian.tools.wrttools.debug.wrtTabGroup"
+ type="org.symbian.tools.wrttools.debug.widget">
+ </launchConfigurationTabGroup>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchConfigurationTypeImages">
+ <launchConfigurationTypeImage
+ configTypeID="org.symbian.tools.wrttools.debug.widget"
+ icon="icons/main16.gif"
+ id="org.symbian.tools.wrttools.debug.widget.image">
+ </launchConfigurationTypeImage>
+ </extension>
+ <extension
+ point="org.eclipse.debug.ui.launchShortcuts">
+ <shortcut
+ class="org.symbian.tools.wrttools.debug.ui.launch.WidgetLaunchShortcut"
+ icon="icons/main16.gif"
+ id="org.symbian.tools.wrttools.debug.wrtshortcut"
+ label="WRT Widget"
+ modes="run, debug">
+ <configurationType
+ id="org.symbian.tools.wrttools.debug.widget">
+ </configurationType>
+ <contextualLaunch>
+ <enablement>
+ <with
+ variable="selection">
+ <count
+ value="1"/>
+ <iterate>
+ <adapt
+ type="org.eclipse.core.resources.IResource">
+ <test
+ forcePluginActivation="true"
+ property="org.symbian.isWrtProject"
+ >
+ </test>
+ </adapt>
+ </iterate>
+ </with>
+ </enablement>
+ <contextLabel
+ label="WRT Widget"
+ mode="run">
+ </contextLabel>
+ <contextLabel
+ label="WRT Widget"
+ mode="debug">
+ </contextLabel></contextualLaunch>
+ </shortcut>
+ </extension>
+ <extension
+ point="org.eclipse.core.expressions.propertyTesters">
+ <propertyTester
+ class="org.symbian.tools.wrttools.debug.internal.property.PropertyTester"
+ id="org.symbian.tools.wrttools.debug.projectTester"
+ namespace="org.symbian"
+ properties="isWrtProject"
+ type="org.eclipse.core.resources.IResource">
+ </propertyTester>
+ </extension>
+ <extension
+ point="org.eclipse.ui.popupMenus">
+ <viewerContribution
+ id="org.symbian.tools.wrttools.debug.core.js"
+ targetID="#JavaScriptRulerContext">
+ <action
+ class="org.eclipse.debug.ui.actions.RulerEnableDisableBreakpointActionDelegate"
+ id="org.chromium.debug.ui.actions.EnableDisableBreakpointRulerActionDelegate"
+ label="Toggle Enablement"
+ menubarPath="debug">
+ </action>
+ <action
+ class="org.eclipse.debug.ui.actions.RulerToggleBreakpointActionDelegate"
+ id="org.chromium.debug.ui.actions.EnableDisableBreakpointAction"
+ label="Toggle Breakpoint"
+ menubarPath="debug">
+ </action>
+ <action
+ class="org.symbian.tools.wrttools.debug.ui.actions.JsBreakpointPropertiesRulerActionDelegate"
+ id="org.symbian.tools.wrttools.debug.core.js.JavaBreakpointPropertiesRulerActionDelegate"
+ label="Breakpoint Properties..."
+ menubarPath="group.properties">
+ </action>
+ </viewerContribution>
+ <viewerContribution
+ id="org.symbian.tools.wrttools.debug.core.ro"
+ targetID="#ReadOnlyJavaScriptRulerContext">
+ <action
+ class="org.eclipse.debug.ui.actions.RulerEnableDisableBreakpointActionDelegate"
+ id="org.chromium.debug.ui.actions.EnableDisableBreakpointRulerActionDelegate"
+ label="Toggle Enablement"
+ menubarPath="debug">
+ </action>
+ <action
+ class="org.eclipse.debug.ui.actions.RulerToggleBreakpointActionDelegate"
+ id="org.chromium.debug.ui.actions.EnableDisableBreakpointAction"
+ label="Toggle Breakpoint"
+ menubarPath="debug">
+ </action>
+ <action
+ class="org.symbian.tools.wrttools.debug.ui.actions.JsBreakpointPropertiesRulerActionDelegate"
+ id="org.symbian.tools.wrttools.debug.core.ro.JavaBreakpointPropertiesRulerActionDelegate"
+ label="Breakpoint Properties..."
+ menubarPath="group.properties">
+ </action>
+ </viewerContribution>
+ </extension>
+ <extension
+ point="org.eclipse.core.runtime.adapters">
+ <factory
+ adaptableType="org.eclipse.wst.jsdt.internal.ui.javaeditor.JavaEditor"
+ class="org.symbian.tools.wrttools.debug.internal.BreakpointAdapterFactory">
+ <adapter
+ type="org.eclipse.debug.ui.actions.IToggleBreakpointsTarget">
+ </adapter>
+ </factory>
+ </extension>
+ <extension
+ point="org.eclipse.ui.editorActions">
+ <editorContribution
+ id="org.symbian.tools.wrttools.debug.core.CompilationUnitEditor.BreakpointRulerActions"
+ targetID="org.eclipse.wst.jsdt.ui.CompilationUnitEditor">
+ <action
+ actionID="RulerDoubleClick"
+ class="org.eclipse.debug.ui.actions.RulerToggleBreakpointActionDelegate"
+ id="org.eclipse.wst.jsdt.debug.ui.actions.ManageBreakpointRulerAction"
+ label="Toggle Breakpoint">
+ </action>
+ </editorContribution>
+ <editorContribution
+ id="org.symbian.tools.wrttools.debug.core.ClassFileEditor.BreakpointRulerActions"
+ targetID="org.eclipse.wst.jsdt.ui.ClassFileEditor">
+ <action
+ actionID="RulerDoubleClick"
+ class="org.eclipse.debug.ui.actions.RulerToggleBreakpointActionDelegate"
+ id="org.eclipse.wst.jsdt.debug.ui.actions.ManageBreakpointRulerAction"
+ label="Toggle Breakpoint">
+ </action>
+ </editorContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectiveExtensions">
+ <perspectiveExtension
+ targetID="org.eclipse.wst.jsdt.ui.JavaPerspective">
+ <actionSet
+ id="org.eclipse.debug.ui.breakpointActionSet">
+ </actionSet>
+ <actionSet
+ id="org.eclipse.debug.ui.debugActionSet">
+ </actionSet>
+ </perspectiveExtension>
+ </extension>
+</plugin>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/Activator.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class Activator extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.symbian.tools.wrttools.debug.core";
+
+ // The shared instance
+ private static Activator plugin;
+
+ /**
+ * The constructor
+ */
+ public Activator() {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static Activator getDefault() {
+ return plugin;
+ }
+
+ public static void log(Throwable e) {
+ log(e.getLocalizedMessage(), e);
+ }
+
+ private static void log(String message, Throwable exception) {
+ getDefault().getLog().log(new Status(IStatus.ERROR, PLUGIN_ID, message, exception));
+ }
+
+ @Override
+ protected void initializeImageRegistry(ImageRegistry reg) {
+ Images.initImageRegistry(reg);
+ }
+
+ public static void log(String message) {
+ log(message, null);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/BreakpointAdapterFactory.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdapterFactory;
+import org.eclipse.debug.ui.actions.IToggleBreakpointsTarget;
+import org.eclipse.ui.IEditorPart;
+
+public class BreakpointAdapterFactory implements IAdapterFactory {
+
+ @SuppressWarnings("unchecked")
+ public Object getAdapter(Object adaptableObject, Class adapterType) {
+ if (adaptableObject instanceof IEditorPart) {
+ IResource resource = (IResource) ((IEditorPart) adaptableObject)
+ .getEditorInput().getAdapter(IResource.class);
+ if (resource != null) {
+ return new WorkspaceLineBreakpointAdapter();
+ }
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Class[] getAdapterList() {
+ return new Class[] { IToggleBreakpointsTarget.class };
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/ChromeDebugUtils.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal;
+
+import java.io.File;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.Platform;
+
+public final class ChromeDebugUtils {
+ public static String getExecutablePath(String folder) {
+ File file = new File(folder);
+ if (file.isDirectory()) {
+ File chromeExecutable = new File(file, getExecutable());
+ if (chromeExecutable.isFile()) {
+ return chromeExecutable.getAbsolutePath();
+ }
+ }
+ return null;
+ }
+
+ private static String getExecutable() {
+ // Add more ifs as we add support for new platforms
+ if (isMac()) {
+ return "Google Chrome.app/Contents/MacOS/Google Chrome";
+ } else {
+ return "chrome.exe";
+ }
+ }
+
+ private ChromeDebugUtils() {
+ // No instantiating
+ }
+
+ public static String getChromeExecutible() {
+ return getExecutablePath(Activator.getDefault().getPreferenceStore().getString(IConstants.PREF_NAME_CHROME_LOCATION));
+ }
+
+ public static boolean isWidgetProject(IProject project) {
+ return project.findMember(IConstants.WRT_PREVIEW_HTML) != null;
+ }
+
+ public static boolean isWindows() {
+ return "windows".equals(Platform.getOS());
+ }
+
+ public static boolean isMac() {
+ return "macosx".equals(Platform.getOS());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/IConstants.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal;
+
+public interface IConstants {
+ String PREF_NAME_CHROME_LOCATION="chrome.location";
+ String PREF_NAME_CHROME_PORT="chrome.port";
+
+ String PROP_PROJECT_NAME = "projectName";
+ public static final String WRT_PREVIEW_HTML = "wrt_preview_frame.html";
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/Images.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.graphics.Image;
+
+public class Images {
+ private static final String WRT16 = "main16.gif";
+
+ public static void initImageRegistry(ImageRegistry registry) {
+ setImage(registry, WRT16);
+ }
+
+ private static void setImage(ImageRegistry registry, String image) {
+ ImageDescriptor img = Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/" + image);
+ registry.put(image, img);
+ }
+
+ public static ImageDescriptor getWrtIcon() {
+ return getImageDescriptor(WRT16);
+ }
+
+ private static ImageDescriptor getImageDescriptor(String image) {
+ return Activator.getDefault().getImageRegistry().getDescriptor(image);
+ }
+
+ public static Image getWrtIconImage() {
+ return getImage(WRT16);
+ }
+
+ private static Image getImage(String image) {
+ return Activator.getDefault().getImageRegistry().get(image);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/PreferenceInitializer.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal;
+
+import java.io.File;
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+public class PreferenceInitializer extends AbstractPreferenceInitializer {
+ private final static String DEFAULT_CHROME_LOCATION = "Local Settings/Application Data/Google/Chrome/Application";
+
+ @Override
+ public void initializeDefaultPreferences() {
+ IPreferenceStore store = Activator.getDefault().getPreferenceStore();
+ File folder = getDefaultFolder();
+ if (ChromeDebugUtils.getExecutablePath(folder.getAbsolutePath()) != null) {
+ store.setDefault(IConstants.PREF_NAME_CHROME_LOCATION, folder
+ .getAbsolutePath());
+ }
+ store.setDefault(IConstants.PREF_NAME_CHROME_PORT, 19222);
+ }
+
+ private File getDefaultFolder() {
+ if (ChromeDebugUtils.isMac()) {
+ return new File("/Applications");
+ }
+ String property = System.getProperty("user.home");
+ File folder = new File(property, DEFAULT_CHROME_LOCATION);
+ return folder;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/WorkspaceLineBreakpointAdapter.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal;
+
+import org.chromium.debug.core.model.LineBreakpointAdapter;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.wst.jsdt.internal.ui.javaeditor.JavaEditor;
+
+public class WorkspaceLineBreakpointAdapter extends LineBreakpointAdapter {
+ @SuppressWarnings("restriction")
+ @Override
+ protected ITextEditor getEditor(IWorkbenchPart part) {
+ if (part instanceof JavaEditor) {
+ return (ITextEditor) part;
+ } else {
+ return null;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/launch/DebugConnectionJob.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal.launch;
+
+import java.net.URI;
+
+import org.chromium.debug.core.model.Destructable;
+import org.chromium.debug.core.model.DestructingGuard;
+import org.chromium.debug.core.model.JavascriptVmEmbedder;
+import org.chromium.debug.core.model.JavascriptVmEmbedderFactory;
+import org.chromium.debug.core.model.NamedConnectionLoggerFactory;
+import org.chromium.debug.core.model.JavascriptVmEmbedder.ConnectionToRemote;
+import org.chromium.sdk.ConnectionLogger;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.symbian.tools.wrttools.debug.internal.Activator;
+import org.symbian.tools.wrttools.debug.internal.model.DebugTargetImpl;
+
+public class DebugConnectionJob extends Job {
+ static final NamedConnectionLoggerFactory NO_CONNECTION_LOGGER_FACTORY = new NamedConnectionLoggerFactory() {
+ public ConnectionLogger createLogger(String title) {
+ return null;
+ }
+ };
+ private URI uri;
+ private final int port;
+ private final ILaunch launch;
+ private final IProject project;
+ private boolean connected = false;
+
+ public DebugConnectionJob(IProject project, final int port, final ILaunch launch) {
+ super("Establish debug connection");
+ this.project = project;
+ this.port = port;
+ this.launch = launch;
+ setUser(false);
+ }
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ connect(monitor);
+ } catch (CoreException e) {
+ return e.getStatus();
+ }
+ return new Status(IStatus.OK, Activator.PLUGIN_ID, "");
+ }
+
+ private void connect(IProgressMonitor monitor) throws CoreException {
+ final DebugTargetImpl target = new DebugTargetImpl(launch, project);
+ final JavascriptVmEmbedder.ConnectionToRemote remoteServer = createConnectionToRemote(
+ port, launch, uri);
+ try {
+
+ DestructingGuard destructingGuard = new DestructingGuard();
+ try {
+ Destructable lauchDestructor = new Destructable() {
+ public void destruct() {
+ if (!launch.hasChildren()) {
+ DebugPlugin.getDefault().getLaunchManager()
+ .removeLaunch(launch);
+ }
+ }
+ };
+
+ destructingGuard.addValue(lauchDestructor);
+
+ Destructable targetDestructor = new Destructable() {
+ public void destruct() {
+ terminateTarget(target);
+ }
+ };
+ destructingGuard.addValue(targetDestructor);
+ boolean attached = target.attach(project.getName(), remoteServer,
+ destructingGuard, new Runnable() {
+ public void run() {
+ openProjectExplorerView(target);
+ }
+ }, monitor);
+ if (!attached) {
+ // Error
+ return;
+ }
+
+ launch.setSourceLocator(target.getSourceLocator());
+
+ launch.addDebugTarget(target);
+ monitor.done();
+
+ // All OK
+ destructingGuard.discharge();
+ } finally {
+ destructingGuard.doFinally();
+ }
+
+ } finally {
+ remoteServer.disposeConnection();
+ }
+ }
+
+ protected ConnectionToRemote createConnectionToRemote(int port,
+ ILaunch launch, URI uri) throws CoreException {
+ return JavascriptVmEmbedderFactory.connectToChromeDevTools(port,
+ NO_CONNECTION_LOGGER_FACTORY, new WidgetTabSelector(uri));
+ }
+
+ protected void openProjectExplorerView(final DebugTargetImpl target) {
+ target.setupBreakpointsFromResources();
+ connected = true;
+ }
+
+ public boolean isConnected() {
+ return connected;
+ }
+
+ private static void terminateTarget(DebugTargetImpl target) {
+ target.setDisconnected(true);
+ target.fireTerminateEvent();
+ }
+
+ public void setTabUri(URI uri) {
+ this.uri = uri;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/launch/WidgetLaunchDelegate.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal.launch;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.text.MessageFormat;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.core.model.ILaunchConfigurationDelegate;
+import org.symbian.tools.wrttools.debug.internal.Activator;
+import org.symbian.tools.wrttools.debug.internal.ChromeDebugUtils;
+import org.symbian.tools.wrttools.debug.internal.IConstants;
+import org.symbian.tools.wrttools.debug.internal.web.WebAppInterface;
+
+public class WidgetLaunchDelegate implements
+ ILaunchConfigurationDelegate {
+ public static final String ID = "org.symbian.tools.wrttools.debug.widget";
+ private static final String[] CHROME_ARGS = {
+ "executable-placeholder", // Chrome executable. Configurable in preferences
+ "port-placeholder", // Here we will set port
+ "profile-placeholder", // Here we will set profile folder so user settings have no effect
+ "--disable-web-security", // Widgets can use network now
+ "--disable-extenions", // Use standard UI, should also re
+ "--disable-plugins", // Run faster!
+ "--no-default-browser-check", // Our users don't need this nagging
+ "--no-first-run", // We don't care
+ "widget-placeholder" // Here we will have widget URI as --app argument
+ };
+ private static final int EXECUTABLE_ARG_NUM = 0;
+ private static final int PORT_ARG_NUM = 1;
+ private static final int PROFILE_ARG_NUM = 2;
+ private static final int APP_ARG_NUM = CHROME_ARGS.length - 1;
+
+ public void launch(ILaunchConfiguration configuration, String mode,
+ final ILaunch launch, IProgressMonitor monitor)
+ throws CoreException {
+ monitor.beginTask("Preparing WRT Debugger", IProgressMonitor.UNKNOWN);
+ // 1. Load all parameters
+ IProject project = getProject(configuration);
+ ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+
+ if (isProjectDebugged(project, launchManager, launch)) {
+ showProjectIsDebugged();
+ launchManager.removeLaunch(launch);
+ throw createCoreException(MessageFormat.format("Project {0} is already running.", project.getName()), null);
+ }
+
+ final int port = Activator.getDefault().getPreferenceStore().getInt(
+ IConstants.PREF_NAME_CHROME_PORT);
+ boolean debug = mode.equals(ILaunchManager.DEBUG_MODE);
+ final URI uri = prepareDebugger(project, debug, launch, port);
+ final String browserExecutable = ChromeDebugUtils.getChromeExecutible();
+ if (browserExecutable == null) {
+ launchManager.removeLaunch(launch);
+ throw createCoreException("No Chrome browser available", null);
+ }
+
+ // 2. Start Chrome
+ synchronized (CHROME_ARGS) { // No chances for collision. Still, better safe then spend several days looking for hard-to-reproduce problem
+ CHROME_ARGS[EXECUTABLE_ARG_NUM] = browserExecutable;
+ CHROME_ARGS[PROFILE_ARG_NUM] = "--user-data-dir=\"" + Activator.getDefault().getStateLocation().append("chromeprofile").toOSString() + "\"";
+ CHROME_ARGS[PORT_ARG_NUM] = "--remote-shell-port=" + port;
+ CHROME_ARGS[APP_ARG_NUM] = MessageFormat.format("--app={0}", uri.toASCIIString());
+ try {
+ Runtime.getRuntime().exec(CHROME_ARGS, null,
+ new File(browserExecutable).getParentFile());
+ } catch (IOException e) {
+ launchManager.removeLaunch(launch);
+ StringBuffer commandLine = new StringBuffer(CHROME_ARGS[0]);
+ for (int i = 1; i < CHROME_ARGS.length; i++) {
+ commandLine.append(" ").append(CHROME_ARGS[i]);
+ }
+ throw createCoreException("Cannot execute: {0}", commandLine
+ .toString(), e);
+ }
+ }
+
+ if (!debug) {
+ launchManager.removeLaunch(launch);
+ }
+ monitor.done();
+ }
+
+
+ private void showProjectIsDebugged() {
+ }
+
+
+ private boolean isProjectDebugged(IProject project, ILaunchManager launchManager, ILaunch l) throws CoreException {
+ ILaunch[] launches = launchManager.getLaunches();
+ for (ILaunch launch : launches) {
+ ILaunchConfiguration launchConfiguration = launch.getLaunchConfiguration();
+ if (!l.equals(launch) && ID.equals(launchConfiguration.getType().getIdentifier())) {
+ IProject p2 = getProject(launchConfiguration);
+ return project.equals(p2);
+ }
+ }
+ return false;
+ }
+
+
+ private URI prepareDebugger(IProject project, boolean debug,
+ final ILaunch launch, final int port) {
+ final DebugConnectionJob job;
+ if (debug) {
+ job = new DebugConnectionJob(project, port, launch);
+ } else {
+ job = null;
+ }
+ final URI uri = WebAppInterface.getInstance().prepareDebugger(project,
+ job);
+ return uri;
+ }
+
+
+ private IProject getProject(ILaunchConfiguration configuration)
+ throws CoreException {
+ String projectName = configuration.getAttribute(
+ IConstants.PROP_PROJECT_NAME, (String) null);
+ if (projectName == null) {
+ throw createCoreException("Project is not selected", null);
+ }
+
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(
+ projectName);
+ if (!project.isAccessible()) {
+ throw createCoreException(MessageFormat.format(
+ "Project {0} is not opened", projectName), null);
+ }
+ return project;
+ }
+
+ private CoreException createCoreException(String message, String arg,
+ Throwable exeption) {
+ return createCoreException(MessageFormat.format(message, arg), exeption);
+ }
+
+ private CoreException createCoreException(String message, Throwable exeption) {
+ return new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ message, exeption));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/launch/WidgetTabSelector.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal.launch;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.util.List;
+
+import org.chromium.debug.core.model.TabSelector;
+import org.chromium.sdk.Browser.TabConnector;
+import org.chromium.sdk.Browser.TabFetcher;
+
+public class WidgetTabSelector implements TabSelector {
+ private final URI uri;
+ private TabConnector connector;
+
+ public WidgetTabSelector(URI uri) {
+ this.uri = uri;
+ }
+
+ public TabConnector selectTab(TabFetcher tabFetcher) throws IOException {
+ // Give it time to start the process/tab. 5 retries, 500 ms inbetween.
+ for (int i = 0; i < 5; i++) {
+ List<? extends TabConnector> tabs = tabFetcher.getTabs();
+ for (TabConnector tabConnector : tabs) {
+ String url = tabConnector.getUrl();
+ try {
+ if (uri.toURL().equals(new URL(url))) {
+ connector = tabConnector;
+ return tabConnector;
+ }
+ } catch (MalformedURLException e) {
+ // Ignore - fails because of "chrome" protocol, we should ignore these tabs anyways
+ }
+ }
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ return null;
+ }
+
+ public TabConnector getConnector() {
+ return connector;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,674 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.symbian.tools.wrttools.debug.internal.model;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.debug.core.model.ChromiumLineBreakpoint;
+import org.chromium.debug.core.model.DebugElementImpl;
+import org.chromium.debug.core.model.Destructable;
+import org.chromium.debug.core.model.DestructingGuard;
+import org.chromium.debug.core.model.IChromiumDebugTarget;
+import org.chromium.debug.core.model.IResourceManager;
+import org.chromium.debug.core.model.JavascriptVmEmbedder;
+import org.chromium.sdk.Breakpoint;
+import org.chromium.sdk.CallFrame;
+import org.chromium.sdk.DebugContext;
+import org.chromium.sdk.DebugEventListener;
+import org.chromium.sdk.ExceptionData;
+import org.chromium.sdk.JavascriptVm;
+import org.chromium.sdk.Script;
+import org.chromium.sdk.DebugContext.State;
+import org.chromium.sdk.DebugContext.StepAction;
+import org.chromium.sdk.JavascriptVm.BreakpointCallback;
+import org.chromium.sdk.JavascriptVm.ScriptsCallback;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IMarkerDelta;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchListener;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.IMemoryBlock;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.ISourceLocator;
+import org.eclipse.debug.core.model.IStackFrame;
+import org.eclipse.debug.core.model.IThread;
+import org.symbian.tools.wrttools.debug.internal.Activator;
+
+/**
+ * An IDebugTarget implementation for remote JavaScript debugging. Can debug any
+ * target that supports the ChromeDevTools protocol.
+ *
+ * Symbian branch is based on Revision 290
+ */
+public class DebugTargetImpl extends DebugElementImpl implements IChromiumDebugTarget {
+
+ private static final IThread[] EMPTY_THREADS = new IThread[0];
+
+ private static final long OPERATION_TIMEOUT_MS = 15000L;
+
+ private final ILaunch launch;
+
+ private final JavascriptThread[] threads;
+
+ private JavascriptVmEmbedder vmEmbedder = STUB_VM_EMBEDDER;
+
+ private ResourceManager resourceManager;
+
+ private DebugContext debugContext;
+
+ private boolean isSuspended = false;
+
+ private boolean isDisconnected = false;
+
+ private final IProject debugProject;
+
+ public DebugTargetImpl(ILaunch launch, IProject project) {
+ super(null);
+ this.launch = launch;
+ this.debugProject = project;
+ this.threads = new JavascriptThread[] { new JavascriptThread(this) };
+ }
+
+ /**
+ * Loads browser tabs, consults the {@code selector} which of the tabs to
+ * attach to, and if any has been selected, requests an attachment to the
+ * tab.
+ *
+ * @param projectNameBase
+ * to create for the browser scripts
+ * @param remoteServer
+ * embedding application we are connected with
+ * @param attachCallback
+ * to invoke on successful attachment
+ * @param monitor
+ * to report the progress to
+ * @return whether the target has attached to a tab
+ * @throws CoreException
+ */
+ public boolean attach(String projectNameBase,
+ JavascriptVmEmbedder.ConnectionToRemote remoteServer,
+ DestructingGuard destructingGuard, Runnable attachCallback,
+ IProgressMonitor monitor) throws CoreException {
+ monitor.beginTask("", 2); //$NON-NLS-1$
+ JavascriptVmEmbedder.VmConnector connector = remoteServer.selectVm();
+ if (connector == null) {
+ return false;
+ }
+ monitor.worked(1);
+ return performAttach(projectNameBase, connector, destructingGuard,
+ attachCallback);
+ }
+
+ private boolean performAttach(String projectNameBase,
+ JavascriptVmEmbedder.VmConnector connector,
+ DestructingGuard destructingGuard, Runnable attachCallback)
+ throws CoreException {
+ final JavascriptVmEmbedder embedder = connector.attach(
+ embedderListener, debugEventListener);
+
+ Destructable embedderDestructor = new Destructable() {
+ public void destruct() {
+ embedder.getJavascriptVm().detach();
+ }
+ };
+
+ destructingGuard.addValue(embedderDestructor);
+
+ vmEmbedder = embedder;
+
+ // We might want to add some url-specific suffix here
+ String projectName = projectNameBase;
+ // We'd like to know when launch is removed to remove our project.
+ DebugPlugin.getDefault().getLaunchManager().addLaunchListener(
+ launchListener);
+ this.resourceManager = createResourceManager();
+ onAttach(projectName, attachCallback);
+ return true;
+ }
+
+ protected ResourceManager createResourceManager() {
+ return new ResourceManager();
+ }
+
+ private void onAttach(String projectName, Runnable attachCallback) {
+ DebugPlugin.getDefault().getBreakpointManager().addBreakpointListener(
+ this);
+ reloadScriptsAndPossiblyResume(attachCallback);
+ }
+
+ public void setupBreakpointsFromResources() {
+ try {
+ IMarker[] markers = debugProject.findMarkers(ChromiumLineBreakpoint.BREAKPOINT_MARKER, true, IResource.DEPTH_INFINITE);
+ Collection<ChromiumLineBreakpoint> breakpoints = new ArrayList<ChromiumLineBreakpoint>(markers.length);
+ for (IMarker marker : markers) {
+ // If it is not ChromiumLineBreakpoint -
+ // something's gone horribly wrong. Better get
+ // ClassCastException
+ ChromiumLineBreakpoint breakpoint = (ChromiumLineBreakpoint) DebugPlugin
+ .getDefault().getBreakpointManager().getBreakpoint(
+ marker);
+ registerBreakpoint(breakpoint, resourceManager
+ .translateResourceToScript(marker.getResource()));
+ breakpoints.add(breakpoint);
+ }
+ } catch (CoreException e) {
+ Activator.log(e);
+ }
+ }
+
+ private void reloadScriptsAndPossiblyResume(final Runnable attachCallback) {
+ reloadScripts(true, new Runnable() {
+ public void run() {
+ try {
+ if (attachCallback != null) {
+ attachCallback.run();
+ }
+ } finally {
+ fireCreationEvent();
+ }
+ Job job = new Job("Update debugger state") {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ debugEventListener.resumedByDefault();
+ return Status.OK_STATUS;
+ }
+ };
+ job.schedule();
+ }
+ });
+ }
+
+ private void reloadScripts(boolean isSync, final Runnable runnable) {
+ Runnable command = new Runnable() {
+ public void run() {
+ vmEmbedder.getJavascriptVm().getScripts(new ScriptsCallback() {
+ public void failure(String errorMessage) {
+ Activator.log(errorMessage);
+ }
+
+ public void success(Collection<Script> scripts) {
+ if (!vmEmbedder.getJavascriptVm().isAttached()) {
+ return;
+ }
+ for (Script script : scripts) {
+ getResourceManager().addScript(script);
+ }
+ if (runnable != null) {
+ runnable.run();
+ }
+ }
+
+ });
+ }
+ };
+ if (isSync) {
+ command.run();
+ return;
+ }
+ Thread t = new Thread(command);
+ t.setDaemon(true);
+ t.start();
+ try {
+ t.join(OPERATION_TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ Activator.log(e);
+ }
+ }
+
+ public String getName() throws DebugException {
+ return "WRT Runtime";
+ }
+
+ public IProcess getProcess() {
+ return null;
+ }
+
+ public JavascriptVmEmbedder getJavascriptEmbedder() {
+ return vmEmbedder;
+ }
+
+ public IThread[] getThreads() throws DebugException {
+ return isDisconnected() ? EMPTY_THREADS : threads;
+ }
+
+ public boolean hasThreads() throws DebugException {
+ return getThreads().length > 0;
+ }
+
+ public boolean supportsBreakpoint(IBreakpoint breakpoint) {
+ return ChromiumDebugPlugin.DEBUG_MODEL_ID.equals(breakpoint
+ .getModelIdentifier())
+ && !isDisconnected();
+ }
+
+ @Override
+ public DebugTargetImpl getDebugTarget() {
+ return this;
+ }
+
+ @Override
+ public ILaunch getLaunch() {
+ return launch;
+ }
+
+ @Override
+ public String getModelIdentifier() {
+ return ChromiumDebugPlugin.DEBUG_MODEL_ID;
+ }
+
+ public boolean canTerminate() {
+ return !isTerminated();
+ }
+
+ public boolean isTerminated() {
+ return isDisconnected();
+ }
+
+ public void terminate() throws DebugException {
+ disconnect();
+ }
+
+ public boolean canResume() {
+ return !isDisconnected() && isSuspended();
+ }
+
+ public synchronized boolean isSuspended() {
+ return isSuspended;
+ }
+
+ private synchronized void setSuspended(boolean isSuspended) {
+ this.isSuspended = isSuspended;
+ }
+
+ public void suspended(int detail) {
+ setSuspended(true);
+ getThread().reset();
+ fireSuspendEvent(detail);
+ }
+
+ public void resume() throws DebugException {
+ debugContext.continueVm(StepAction.CONTINUE, 1, null);
+ // Let's pretend Chromium does respond to the "continue" request
+ // immediately
+ resumed(DebugEvent.CLIENT_REQUEST);
+ }
+
+ public void resumed(int detail) {
+ fireResumeEvent(detail);
+ }
+
+ public boolean canSuspend() {
+ return !isDisconnected() && !isSuspended();
+ }
+
+ public void suspend() throws DebugException {
+ vmEmbedder.getJavascriptVm().suspend(null);
+ }
+
+ public boolean canDisconnect() {
+ return !isDisconnected();
+ }
+
+ public void disconnect() throws DebugException {
+ if (!canDisconnect()) {
+ return;
+ }
+ if (!vmEmbedder.getJavascriptVm().detach()) {
+ Activator
+ .log("Received bad result from browser while disconnecting");
+ }
+ // This is a duplicated call to disconnected().
+ // The primary one comes from V8DebuggerToolHandler#onDebuggerDetached
+ // but we want to make sure the target becomes disconnected even if
+ // there is a browser failure and it does not respond.
+ debugEventListener.disconnected();
+ }
+
+ public synchronized boolean isDisconnected() {
+ return isDisconnected;
+ }
+
+ public IMemoryBlock getMemoryBlock(long startAddress, long length)
+ throws DebugException {
+ return null;
+ }
+
+ public boolean supportsStorageRetrieval() {
+ return false;
+ }
+
+ public IProject getDebugProject() {
+ return debugProject;
+ }
+
+ /**
+ * Fires a debug event
+ *
+ * @param event
+ * to be fired
+ */
+ public void fireEvent(DebugEvent event) {
+ DebugPlugin debugPlugin = DebugPlugin.getDefault();
+ if (debugPlugin != null) {
+ debugPlugin.fireDebugEventSet(new DebugEvent[] { event });
+ }
+ }
+
+ public void fireEventForThread(int kind, int detail) {
+ try {
+ IThread[] threads = getThreads();
+ if (threads.length > 0) {
+ fireEvent(new DebugEvent(threads[0], kind, detail));
+ }
+ } catch (DebugException e) {
+ // Actually, this is not thrown in our getThreads()
+ return;
+ }
+ }
+
+ public void fireCreationEvent() {
+ setDisconnected(false);
+ fireEventForThread(DebugEvent.CREATE, DebugEvent.UNSPECIFIED);
+ }
+
+ public synchronized void setDisconnected(boolean disconnected) {
+ isDisconnected = disconnected;
+ }
+
+ public void fireResumeEvent(int detail) {
+ setSuspended(false);
+ fireEventForThread(DebugEvent.RESUME, detail);
+ fireEvent(new DebugEvent(this, DebugEvent.RESUME, detail));
+ }
+
+ public void fireSuspendEvent(int detail) {
+ setSuspended(true);
+ fireEventForThread(DebugEvent.SUSPEND, detail);
+ fireEvent(new DebugEvent(this, DebugEvent.SUSPEND, detail));
+ }
+
+ public void fireTerminateEvent() {
+ // TODO(peter.rybin): from Alexander Pavlov: I think you need to fire a
+ // terminate event after
+ // this line, for consolePseudoProcess if one is not null.
+ fireEventForThread(DebugEvent.TERMINATE, DebugEvent.UNSPECIFIED);
+ fireEvent(new DebugEvent(this, DebugEvent.TERMINATE,
+ DebugEvent.UNSPECIFIED));
+ fireEvent(new DebugEvent(getLaunch(), DebugEvent.TERMINATE,
+ DebugEvent.UNSPECIFIED));
+ }
+
+ public void breakpointAdded(IBreakpoint breakpoint) {
+ if (!supportsBreakpoint(breakpoint)) {
+ return;
+ }
+ try {
+ if (breakpoint.isEnabled()) {
+ // Class cast is ensured by the supportsBreakpoint
+ // implementation
+ final ChromiumLineBreakpoint lineBreakpoint = (ChromiumLineBreakpoint) breakpoint;
+ IFile file = (IFile) breakpoint.getMarker().getResource();
+ if (!getResourceManager().isAddingFile(file)) {
+ final Script script = getResourceManager().getScript(file);
+ if (script != null) {
+ registerBreakpoint(lineBreakpoint, script.getName());
+ }
+ }
+ }
+ } catch (CoreException e) {
+ Activator.log(e);
+ }
+ }
+
+ public void registerBreakpoint(final ChromiumLineBreakpoint breakpoint,
+ final String script) throws CoreException {
+ final int line = (breakpoint.getLineNumber() - 1);
+ BreakpointCallback callback = new BreakpointCallback() {
+ public void success(Breakpoint b) {
+ breakpoint.setBreakpoint(b);
+ }
+
+ public void failure(String errorMessage) {
+ Activator.log(errorMessage);
+ }
+ };
+ // ILineBreakpoint lines are 1-based while V8 lines are 0-based
+ JavascriptVm javascriptVm = vmEmbedder.getJavascriptVm();
+ if (script != null) {
+ javascriptVm.setBreakpoint(Breakpoint.Type.SCRIPT_NAME, script
+ ,line, Breakpoint.EMPTY_VALUE, breakpoint
+ .isEnabled(), breakpoint.getCondition(), breakpoint
+ .getIgnoreCount(), callback);
+ } else {
+ javascriptVm.setBreakpoint(Breakpoint.Type.SCRIPT_ID, String
+ .valueOf(script), line, Breakpoint.EMPTY_VALUE,
+ breakpoint.isEnabled(), breakpoint.getCondition(),
+ breakpoint.getIgnoreCount(), callback);
+ }
+ }
+
+ public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
+ if (!supportsBreakpoint(breakpoint)) {
+ return;
+ }
+ // Class cast is ensured by the supportsBreakpoint implementation
+ ((ChromiumLineBreakpoint) breakpoint).changed();
+ }
+
+ public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
+ if (!supportsBreakpoint(breakpoint)) {
+ return;
+ }
+ try {
+ if (breakpoint.isEnabled()) {
+ // Class cast is ensured by the supportsBreakpoint
+ // implementation
+ ChromiumLineBreakpoint lineBreakpoint = (ChromiumLineBreakpoint) breakpoint;
+ lineBreakpoint.clear();
+ }
+ } catch (CoreException e) {
+ Activator.log(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (ILaunch.class.equals(adapter)) {
+ return this.launch;
+ }
+ return super.getAdapter(adapter);
+ }
+
+ public IResourceManager getResourceManager() {
+ return resourceManager;
+ }
+
+ public JavascriptThread getThread() {
+ return isDisconnected() ? null : threads[0];
+ }
+
+ private static void breakpointsHit(
+ Collection<? extends Breakpoint> breakpointsHit) {
+ if (breakpointsHit.isEmpty()) {
+ return;
+ }
+ IBreakpoint[] breakpoints = DebugPlugin.getDefault()
+ .getBreakpointManager().getBreakpoints(
+ ChromiumDebugPlugin.DEBUG_MODEL_ID);
+ for (IBreakpoint breakpoint : breakpoints) {
+ ChromiumLineBreakpoint jsBreakpoint = (ChromiumLineBreakpoint) breakpoint;
+ if (breakpointsHit.contains(jsBreakpoint.getBrowserBreakpoint())) {
+ jsBreakpoint.setIgnoreCount(-1); // reset ignore count as we've
+ // hit it
+ }
+ }
+ }
+
+ private static String trim(String text, int maxLength) {
+ if (text == null || text.length() <= maxLength) {
+ return text;
+ }
+ return text.substring(0, maxLength - 3) + "..."; //$NON-NLS-1$
+ }
+
+ public DebugContext getDebugContext() {
+ return debugContext;
+ }
+
+ public ISourceLocator getSourceLocator() {
+ return sourceLocator;
+ }
+
+ private final DebugEventListenerImpl debugEventListener = new DebugEventListenerImpl();
+
+ class DebugEventListenerImpl implements DebugEventListener {
+ // Synchronizes calls from ReaderThread of Connection and one call from
+ // some worker thread
+ private final Object suspendResumeMonitor = new Object();
+ private boolean alreadyResumedOrSuspended = false;
+
+ public void disconnected() {
+ if (!isDisconnected()) {
+ setDisconnected(true);
+ DebugPlugin.getDefault().getBreakpointManager()
+ .removeBreakpointListener(DebugTargetImpl.this);
+ fireTerminateEvent();
+ }
+ }
+
+ public void resumedByDefault() {
+ synchronized (suspendResumeMonitor) {
+ if (!alreadyResumedOrSuspended) {
+ resumed();
+ }
+ }
+ }
+
+ public void resumed() {
+ synchronized (suspendResumeMonitor) {
+ DebugTargetImpl.this.resumed(DebugEvent.CLIENT_REQUEST);
+ alreadyResumedOrSuspended = true;
+ }
+ }
+
+ public void scriptLoaded(Script newScript) {
+ getResourceManager().addScript(newScript);
+ }
+
+ public void suspended(DebugContext context) {
+ synchronized (suspendResumeMonitor) {
+ DebugTargetImpl.this.debugContext = context;
+ breakpointsHit(context.getBreakpointsHit());
+ int suspendedDetail;
+ if (context.getState() == State.EXCEPTION) {
+ logExceptionFromContext(context);
+ suspendedDetail = DebugEvent.BREAKPOINT;
+ } else {
+ if (context.getBreakpointsHit().isEmpty()) {
+ suspendedDetail = DebugEvent.STEP_END;
+ } else {
+ suspendedDetail = DebugEvent.BREAKPOINT;
+ }
+ }
+ DebugTargetImpl.this.suspended(suspendedDetail);
+
+ alreadyResumedOrSuspended = true;
+ }
+ }
+ }
+
+ private void logExceptionFromContext(DebugContext context) {
+ ExceptionData exceptionData = context.getExceptionData();
+ CallFrame topFrame = context.getCallFrames().get(0);
+ Script script = topFrame.getScript();
+ Activator.log(MessageFormat.format("{0} {1} (in {2}:{3}): {4}", exceptionData
+ .isUncaught() ? "Uncaught" : "Caught", exceptionData
+ .getExceptionMessage(), script != null ? script.getName()
+ : "<unknown>", //$NON-NLS-1$
+ topFrame.getLineNumber(), trim(exceptionData.getSourceText(),
+ 80)));
+ }
+
+ private final JavascriptVmEmbedder.Listener embedderListener = new JavascriptVmEmbedder.Listener() {
+ public void reset() {
+ getResourceManager().clear();
+ fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.STATE));
+ }
+
+ public void closed() {
+ fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.STATE));
+ }
+ };
+
+ private final ILaunchListener launchListener = new ILaunchListener() {
+ public void launchAdded(ILaunch launch) {
+ }
+
+ public void launchChanged(ILaunch launch) {
+ }
+
+ // TODO(peter.rybin): maybe have one instance of listener for all
+ // targets?
+ public void launchRemoved(ILaunch launch) {
+ if (launch != DebugTargetImpl.this.launch) {
+ return;
+ }
+ DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(
+ this);
+ }
+ };
+
+ private final static JavascriptVmEmbedder STUB_VM_EMBEDDER = new JavascriptVmEmbedder() {
+ public JavascriptVm getJavascriptVm() {
+ // TODO(peter.rybin): decide and redo this exception
+ throw new UnsupportedOperationException();
+ }
+
+ public String getTargetName() {
+ // TODO(peter.rybin): decide and redo this exception
+ throw new UnsupportedOperationException();
+ }
+
+ public String getThreadName() {
+ // TODO(peter.rybin): decide and redo this exception
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ /**
+ * This very simple source locator works because we provide our own source
+ * files. We'll have to try harder, once we link with resource js files.
+ */
+ private final ISourceLocator sourceLocator = new ISourceLocator() {
+ public Object getSourceElement(IStackFrame stackFrame) {
+ if (stackFrame instanceof StackFrame == false) {
+ return null;
+ }
+ StackFrame jsStackFrame = (StackFrame) stackFrame;
+
+ Script script = jsStackFrame.getCallFrame().getScript();
+ if (script == null) {
+ return null;
+ }
+
+ return resourceManager.getResource(script);
+ }
+ };
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/model/JavascriptThread.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,69 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.symbian.tools.wrttools.debug.internal.model;
+
+import java.util.List;
+
+import org.chromium.sdk.CallFrame;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * This class represents the only Chromium V8 VM thread.
+ *
+ * Symbian branch is based on revision 234
+ */
+public class JavascriptThread extends
+ org.chromium.debug.core.model.JavascriptThread {
+
+ private static final StackFrame[] EMPTY_FRAMES = new StackFrame[0];
+
+ /**
+ * Cached stack
+ */
+ private StackFrame[] stackFrames;
+
+ /**
+ * Constructs a new thread for the given target
+ *
+ * @param debugTarget
+ * this thread is created for
+ */
+ public JavascriptThread(DebugTargetImpl debugTarget) {
+ super(debugTarget);
+ }
+
+ public org.chromium.debug.core.model.StackFrame[] getStackFrames()
+ throws DebugException {
+ if (isSuspended()) {
+ ensureStackFrames();
+ return stackFrames;
+ } else {
+ return EMPTY_FRAMES;
+ }
+ }
+
+ public void reset() {
+ this.stackFrames = null;
+ }
+
+ private void ensureStackFrames() {
+ this.stackFrames = wrapStackFrames(getDebugTarget().getDebugContext()
+ .getCallFrames());
+ }
+
+ private StackFrame[] wrapStackFrames(List<? extends CallFrame> jsFrames) {
+ StackFrame[] frames = new StackFrame[jsFrames.size()];
+ for (int i = 0, size = frames.length; i < size; ++i) {
+ frames[i] = new StackFrame(getDebugTarget(), this, jsFrames.get(i));
+ }
+ return frames;
+ }
+
+ public String getName() throws DebugException {
+ return NLS.bind("JavaScript Thread ({0})",
+ (isSuspended() ? "Suspended" : "Running")); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/model/ResourceManager.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,138 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.symbian.tools.wrttools.debug.internal.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.chromium.debug.core.model.IResourceManager;
+import org.chromium.sdk.Script;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.symbian.tools.wrttools.debug.internal.web.WorkspaceResourcesServlet;
+
+/**
+ * This object handles the mapping between {@link Script}s and their
+ * corresponding resources inside Eclipse.
+ *
+ * Symbian branch is currently based on Revision 138
+ */
+public class ResourceManager implements IResourceManager {
+ /**
+ * Script identifier for a breakpoint location.
+ */
+ public static class ScriptIdentifier {
+ public static ScriptIdentifier forScript(Script script) {
+ String name = script.getName();
+ return new ScriptIdentifier(name, name != null ? -1 : script
+ .getId(), script.getStartLine(), script.getEndLine());
+ }
+
+ private final int endLine;
+ private final long id;
+ private final String name;
+ private final int startLine;
+
+ private ScriptIdentifier(String name, long id, int startLine,
+ int endLine) {
+ this.name = name;
+ this.id = id;
+ this.startLine = startLine;
+ this.endLine = endLine;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ScriptIdentifier)) {
+ return false;
+ }
+ ScriptIdentifier that = (ScriptIdentifier) obj;
+ if (this.startLine != that.startLine
+ || this.endLine != that.endLine) {
+ return false;
+ }
+ if (name == null) {
+ // an unnamed script, only id is known
+ return that.name == null && this.id == that.id;
+ }
+ // a named script
+ return this.name.equals(that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (id ^ (id >>> 32));
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + 17 * startLine + 19 * endLine;
+ return result;
+ }
+ }
+
+ private Object fileBeingAdded;
+ private final Map<IFile, Script> resourceToScript = new HashMap<IFile, Script>();
+ private final Map<ScriptIdentifier, IFile> scriptIdToResource = new HashMap<ScriptIdentifier, IFile>();
+
+ public synchronized void addScript(Script script) {
+ IFile scriptFile = getResource(script);
+ if (scriptFile == null) {
+ scriptFile = getFile(script.getName());
+ if (scriptFile != null) {
+ fileBeingAdded = scriptFile;
+ try {
+ putScript(script, scriptFile);
+ } finally {
+ fileBeingAdded = null;
+ }
+ }
+ }
+ }
+
+ public synchronized void clear() {
+ resourceToScript.clear();
+ scriptIdToResource.clear();
+ }
+
+ private IFile getFile(String name) {
+ if (name == null) {
+ return null;
+ }
+ IFile file = WorkspaceResourcesServlet.getFileFromUrl(name);
+ if (file != null && !file.isAccessible()) {
+ file = null;
+ }
+ return file;
+ }
+
+ public synchronized IFile getResource(Script script) {
+ return scriptIdToResource.get(ScriptIdentifier.forScript(script));
+ }
+
+ public synchronized Script getScript(IFile resource) {
+ return resourceToScript.get(resource);
+ }
+
+ /**
+ * @return whether the given file is being added to the target project
+ */
+ public boolean isAddingFile(IFile file) {
+ return file.equals(fileBeingAdded);
+ }
+
+ public synchronized void putScript(Script script, IFile resource) {
+ ScriptIdentifier scriptId = ScriptIdentifier.forScript(script);
+ resourceToScript.put(resource, script);
+ scriptIdToResource.put(scriptId, resource);
+ }
+
+ public synchronized boolean scriptHasResource(Script script) {
+ return getResource(script) != null;
+ }
+
+ public String translateResourceToScript(IResource resource) {
+ return WorkspaceResourcesServlet.getHttpUrl(resource);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/model/StackFrame.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,54 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.symbian.tools.wrttools.debug.internal.model;
+
+import org.chromium.debug.core.model.IChromiumDebugTarget;
+import org.chromium.sdk.CallFrame;
+import org.chromium.sdk.Script;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * An IStackFrame implementation over a JsStackFrame instance.
+ *
+ * Symbian branch is based on revision 261
+ */
+public class StackFrame extends org.chromium.debug.core.model.StackFrame {
+ private final CallFrame stackFrame;
+
+ /**
+ * Constructs a stack frame for the given handler using the FrameMirror data
+ * from the remote V8 VM.
+ *
+ * @param debugTarget
+ * the global parent
+ * @param thread
+ * for which the stack frame is created
+ * @param stackFrame
+ * an underlying SDK stack frame
+ */
+ public StackFrame(IChromiumDebugTarget debugTarget,
+ JavascriptThread thread, CallFrame stackFrame) {
+ super(debugTarget, thread, stackFrame);
+ this.stackFrame = stackFrame;
+ }
+
+ public String getName() throws DebugException {
+ String name = stackFrame.getFunctionName();
+ Script script = stackFrame.getScript();
+ if (script == null) {
+ return "<unknown>";
+ }
+ IFile resource = getDebugTarget().getResourceManager().getResource(
+ script);
+ int line = script.getStartLine() + getLineNumber();
+ if (line != -1) {
+ name = NLS.bind("{0} [{1}:{2}]", new Object[] { name,
+ resource.getProjectRelativePath().toString(), line });
+ }
+ return name;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/property/PropertyTester.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal.property;
+
+import org.eclipse.core.resources.IResource;
+import org.symbian.tools.wrttools.debug.internal.ChromeDebugUtils;
+
+public class PropertyTester extends org.eclipse.core.expressions.PropertyTester {
+
+ public boolean test(Object receiver, String property, Object[] args,
+ Object expectedValue) {
+ if (property.equals("isWrtProject")) {
+ return ChromeDebugUtils.isWidgetProject(((IResource) receiver).getProject());
+ }
+ return false;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/web/EmulatorContext.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal.web;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.osgi.service.http.HttpContext;
+
+public class EmulatorContext implements HttpContext {
+
+ public String getMimeType(String name) {
+ return null;
+ }
+
+ public URL getResource(String name) {
+ try {
+ return new URL("file:///e:/maven-prefs.png");
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ }
+
+ public boolean handleSecurity(HttpServletRequest request,
+ HttpServletResponse response) throws IOException {
+ return true;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/web/WebAppInterface.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal.web;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.jobs.Job;
+import org.symbian.tools.wrttools.debug.internal.Activator;
+import org.symbian.tools.wrttools.debug.internal.launch.DebugConnectionJob;
+
+public class WebAppInterface {
+ private static WebAppInterface INSTANCE;
+
+ public static void connectDebugger(String widget, String id) {
+ getInstance().connect(widget, id);
+ }
+
+ public static String decode(String value) {
+ try {
+ return URLDecoder.decode(value, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String encode(String project) {
+ try {
+ return URLEncoder.encode(project, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String getAjaxUri(String widget, String id) {
+ return getInstance().createAjaxUri(widget, id).toASCIIString();
+ }
+
+ public synchronized static WebAppInterface getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new WebAppInterface();
+ }
+ return INSTANCE;
+ }
+
+ public static String getUrl(String widget, String id) {
+ return getInstance().complete(widget, id);
+ }
+
+ public static boolean isConnected(String widget, String id) {
+ return getInstance().isJobComplete(widget, id);
+ }
+
+ private final Map<String, DebugConnectionJob> debuggerJobs = new TreeMap<String, DebugConnectionJob>();
+
+ private WebAppInterface() {
+ try {
+ WebappManager.start("wrtbrowser");
+ } catch (Exception e) {
+ Activator.log(e);
+ }
+ }
+
+ private synchronized String complete(String widget, String id) {
+ IFile file = ResourcesPlugin.getWorkspace().getRoot()
+ .getProject(widget).getFile("wrt_preview_frame.html");
+ if (file.isAccessible()) {
+ return WorkspaceResourcesServlet.getHttpUrl(file);
+ }
+ return "";
+ }
+
+ private synchronized void connect(String widget, String id) {
+ Job job = debuggerJobs.get(getId(widget, id));
+ if (job != null) {
+ job.schedule();
+ }
+ }
+
+ private URI createAjaxUri(String widget, String id) {
+ try {
+ return createUri("connectionTest.jsp", widget, id);
+ } catch (URISyntaxException e) {
+ Activator.log(e);
+ return null;
+ }
+ }
+
+ private URI createUri(String page, String project, String session)
+ throws URISyntaxException {
+ URI uri = new URI("http", null, WebappManager.getHost(), WebappManager
+ .getPort(), "/wrtdebugger/" + page, "widget=" + encode(project)
+ + "&session=" + session, null);
+ return uri;
+ }
+
+ private String getId(String name, String session) {
+ return name + "$" + session;
+ }
+
+ private synchronized boolean isJobComplete(String widget, String id) {
+ DebugConnectionJob job = debuggerJobs.get(getId(widget, id));
+ boolean isComplete = job == null || job.isConnected();
+ return isComplete;
+ }
+
+ public synchronized URI prepareDebugger(IProject project,
+ DebugConnectionJob job) {
+ try {
+ String session = Long.toHexString(System.currentTimeMillis());
+ URI uri = createUri("debugger.jsp", project.getName(), session);
+ if (job != null) {
+ debuggerJobs.put(getId(project.getName(), session), job);
+ job.setTabUri(uri);
+ }
+ return uri;
+ } catch (URISyntaxException e) {
+ Activator.log(e);
+ }
+ return null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/web/WebappManager.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal.web;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.equinox.http.jetty.JettyConfigurator;
+import org.eclipse.equinox.jsp.jasper.JspServlet;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.HttpService;
+import org.symbian.tools.wrttools.debug.internal.Activator;
+
+/**
+ * Copy from the WS Explorer
+ *
+ * @author Eugene Ostroukhov
+ *
+ */
+public class WebappManager {
+ public static final String WORKSPACE_RESOURCES_CONTEXT = "/wspace";
+ public static final String STATIC_RESOURCES_CONTEXT = "/wrtdebugger";
+ public static final String WEB_CONTENT_ROOT = "/http-content";
+
+ private static String host;
+ private static int port = -1;
+ private static final int AUTO_SELECT_JETTY_PORT = 0;
+
+ @SuppressWarnings("unchecked")
+ public static void start(String webappName) throws Exception {
+ Dictionary d = new Hashtable();
+
+ d.put("http.port", new Integer(getPortParameter())); //$NON-NLS-1$
+
+ // set the base URL
+// d.put("context.path", "/wrtdebugger"); //$NON-NLS-1$ //$NON-NLS-2$
+ d.put("other.info", "org.symbian.wst.debugger"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ // suppress Jetty INFO/DEBUG messages to stderr
+ Logger.getLogger("org.mortbay").setLevel(Level.WARNING); //$NON-NLS-1$
+
+ JettyConfigurator.startServer(webappName, d);
+ checkBundle();
+ Bundle bundle = Activator.getDefault().getBundle();
+ HttpService service = (HttpService) bundle.getBundleContext().getService(getServiceReference());
+ HttpContext httpContext = service.createDefaultHttpContext();
+ service.registerResources(STATIC_RESOURCES_CONTEXT, WEB_CONTENT_ROOT, httpContext);
+ service.registerServlet(WORKSPACE_RESOURCES_CONTEXT, new WorkspaceResourcesServlet(), new Hashtable(), httpContext);
+ service.registerServlet(STATIC_RESOURCES_CONTEXT + "/*.jsp", new JspServlet(bundle, WEB_CONTENT_ROOT, STATIC_RESOURCES_CONTEXT + "*.jsp"), new Hashtable(), httpContext);
+ }
+
+ /*
+ * Ensures that the bundle with the specified name and the highest available
+ * version is started and reads the port number
+ */
+ private static void checkBundle() throws InvalidSyntaxException, BundleException {
+ Bundle bundle = Platform.getBundle("org.eclipse.equinox.http.registry"); //$NON-NLS-1$if (bundle != null) {
+ if (bundle.getState() == Bundle.RESOLVED) {
+ bundle.start(Bundle.START_TRANSIENT);
+ }
+ if (port == -1) {
+ ServiceReference reference = getServiceReference();
+ Object assignedPort = reference.getProperty("http.port"); //$NON-NLS-1$
+ port = Integer.parseInt((String)assignedPort);
+ }
+ }
+
+ private static ServiceReference getServiceReference()
+ throws InvalidSyntaxException {
+ Bundle bundle2 = Activator.getDefault().getBundle();
+ // Jetty selected a port number for us
+ ServiceReference[] reference = bundle2.getBundleContext().getServiceReferences("org.osgi.service.http.HttpService", "(other.info=org.symbian.wst.debugger)"); //$NON-NLS-1$ //$NON-NLS-2$
+ return reference[0];
+ }
+
+ public static void stop(String webappName) throws CoreException {
+ try {
+ JettyConfigurator.stopServer(webappName);
+ }
+ catch (Exception e) {
+ //HelpBasePlugin.logError("An error occured while stopping the help server", e); //$NON-NLS-1$
+ }
+ }
+
+ public static int getPort() {
+ return port;
+ }
+
+ /*
+ * Get the port number which will be passed to Jetty
+ */
+ private static int getPortParameter() {
+ if (port == -1) {
+ return AUTO_SELECT_JETTY_PORT;
+ }
+ return port;
+ }
+
+ public static String getHost() {
+ if (host == null) {
+ host = "127.0.0.1"; //$NON-NLS-1$
+ }
+ return host;
+ }
+
+ private WebappManager() {
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/web/WorkspaceResourcesServlet.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.internal.web;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.StringTokenizer;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.symbian.tools.wrttools.debug.internal.Activator;
+
+public class WorkspaceResourcesServlet extends HttpServlet {
+ private static final long serialVersionUID = -3217197074249607950L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(
+ new Path(req.getPathInfo()));
+ if (file.isAccessible()) {
+ InputStream contents = null;
+ try {
+ contents = file.getContents();
+ byte[] buf = new byte[4048];
+ int i;
+ while ((i = contents.read(buf)) >= 0) {
+ resp.getOutputStream().write(buf, 0, i);
+ }
+ } catch (CoreException e) {
+ Activator.log(e);
+ } finally {
+ if (contents != null) {
+ contents.close();
+ }
+ }
+ } else {
+ resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ }
+ }
+
+ public static String getHttpUrl(IResource file) {
+ try {
+ String path = WebappManager.WORKSPACE_RESOURCES_CONTEXT
+ + (file != null ? encode(file.getFullPath()) : "/");
+ URL url = new URL("http", WebappManager.getHost(), WebappManager
+ .getPort(), path);
+ return url.toString();
+ } catch (MalformedURLException e) {
+ return file.getLocationURI().toString();
+ }
+ }
+
+ private static String encode(IPath fullPath) {
+ try {
+ StringBuffer result = new StringBuffer();
+ String[] segments = fullPath.segments();
+ for (int i = 0; i < segments.length; i++) {
+ String string = segments[i];
+ result.append("/");
+ // java.net.URLEncoder encodes " " as "+" while Chrome needs "%20"
+ StringTokenizer tokenizer = new StringTokenizer(string, " ", false);
+ while (tokenizer.hasMoreElements()) {
+ result.append(URLEncoder.encode(tokenizer.nextToken(),
+ "UTF-8"));
+ if (tokenizer.hasMoreTokens()) {
+ result.append("%20");
+ }
+ }
+ }
+ return result.toString();
+ } catch (UnsupportedEncodingException e) {
+ // Something is horribly wrong - JRE doesn't have UTF8?
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static IFile getFileFromUrl(String name) {
+ try {
+ String root = getHttpUrl(null);
+ IFile file = null;
+ if (name.startsWith(root)) {
+ String fileName = name.substring(root.length());
+ fileName = URLDecoder.decode(fileName, "UTF-8");
+ final IPath path = new Path(fileName);
+ file = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
+ if (!file.isAccessible()) {
+ return null;
+ }
+ }
+ return file;
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/ui/DebugPreferencePage.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.ui;
+
+import org.eclipse.jface.preference.DirectoryFieldEditor;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.symbian.tools.wrttools.debug.internal.Activator;
+import org.symbian.tools.wrttools.debug.internal.IConstants;
+
+public class DebugPreferencePage extends FieldEditorPreferencePage implements
+ IWorkbenchPreferencePage {
+
+ public DebugPreferencePage() {
+ super(GRID);
+ setPreferenceStore(Activator.getDefault().getPreferenceStore());
+ setDescription("WRT debugger settings");
+ }
+
+ @Override
+ protected void createFieldEditors() {
+ DirectoryFieldEditor editor = new DirectoryFieldEditor("chrome", "Chrome Install Location:", getFieldEditorParent());
+ editor.setPreferenceName(IConstants.PREF_NAME_CHROME_LOCATION);
+ addField(editor);
+ }
+
+ public void init(IWorkbench workbench) {
+ // Do nothing
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/ui/actions/JsBreakpointPropertiesRulerAction.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.symbian.tools.wrttools.debug.ui.actions;
+
+import org.chromium.debug.core.model.ChromiumLineBreakpoint;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.ui.actions.RulerBreakpointAction;
+import org.eclipse.jface.text.source.IVerticalRulerInfo;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.ui.dialogs.PropertyDialogAction;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.ui.texteditor.IUpdate;
+
+/**
+ * Action to bring up the breakpoint properties dialog.
+ */
+public class JsBreakpointPropertiesRulerAction extends RulerBreakpointAction
+ implements IUpdate {
+ private IBreakpoint breakpoint;
+
+ public JsBreakpointPropertiesRulerAction(ITextEditor editor,
+ IVerticalRulerInfo rulerInfo) {
+ super(editor, rulerInfo);
+ setText("Breakpoint Properties...");
+ }
+
+ @Override
+ public void run() {
+ if (getBreakpoint() != null) {
+ PropertyDialogAction action = new PropertyDialogAction(getEditor()
+ .getEditorSite(), new ISelectionProvider() {
+ public void addSelectionChangedListener(
+ ISelectionChangedListener listener) {
+ }
+
+ public ISelection getSelection() {
+ return new StructuredSelection(getBreakpoint());
+ }
+
+ public void removeSelectionChangedListener(
+ ISelectionChangedListener listener) {
+ }
+
+ public void setSelection(ISelection selection) {
+ }
+ });
+ action.run();
+ }
+ }
+
+ public void update() {
+ breakpoint = null;
+ IBreakpoint activeBreakpoint = getBreakpoint();
+ if (activeBreakpoint != null
+ && activeBreakpoint instanceof ChromiumLineBreakpoint) {
+ breakpoint = activeBreakpoint;
+ }
+ setEnabled(breakpoint != null);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/ui/actions/JsBreakpointPropertiesRulerActionDelegate.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.symbian.tools.wrttools.debug.ui.actions;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.text.source.IVerticalRulerInfo;
+import org.eclipse.ui.texteditor.AbstractRulerActionDelegate;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+/**
+ * Delegate for JsBreakpointPropertiesRulerAction.
+ */
+public class JsBreakpointPropertiesRulerActionDelegate extends AbstractRulerActionDelegate {
+
+ @Override
+ protected IAction createAction(ITextEditor editor, IVerticalRulerInfo rulerInfo) {
+ return new JsBreakpointPropertiesRulerAction(editor, rulerInfo);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/ui/launch/WidgetBasicTab.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.ui.launch;
+
+import java.util.LinkedList;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+import org.symbian.tools.wrttools.debug.internal.Activator;
+import org.symbian.tools.wrttools.debug.internal.ChromeDebugUtils;
+import org.symbian.tools.wrttools.debug.internal.IConstants;
+import org.symbian.tools.wrttools.debug.internal.Images;
+
+public class WidgetBasicTab extends AbstractLaunchConfigurationTab {
+ private ComboViewer project;
+ private boolean canSave;
+
+ @Override
+ public Image getImage() {
+ return Images.getWrtIconImage();
+ }
+
+ public void createControl(Composite parent) {
+ Composite root = new Composite(parent, SWT.NONE);
+ FormLayout layout = new FormLayout();
+ layout.marginWidth = 5;
+ layout.marginHeight = 5;
+ layout.spacing = 5;
+ root.setLayout(layout);
+
+ Label label = new Label(root, SWT.NONE);
+ label.setText("WRT Widget Project:");
+
+ project = new ComboViewer(root, SWT.READ_ONLY);
+ project.setContentProvider(new ArrayContentProvider());
+ project.setLabelProvider(new WorkbenchLabelProvider());
+
+ FormData formData = new FormData();
+ formData.left = new FormAttachment(label);
+ formData.right = new FormAttachment(100, 0);
+ project.getControl().setLayoutData(formData);
+
+ project.setInput(getWidgetProjects());
+ project.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ setDirty(true);
+ validate();
+ }
+ });
+ setControl(root);
+ }
+
+ private IProject[] getWidgetProjects() {
+ IProject[] projects = ResourcesPlugin.getWorkspace().getRoot()
+ .getProjects();
+ LinkedList<IProject> filtered = new LinkedList<IProject>();
+ for (IProject p : projects) {
+ if (ChromeDebugUtils.isWidgetProject(p)) {
+ filtered.add(p);
+ }
+ }
+ return filtered.toArray(new IProject[filtered.size()]);
+ }
+
+ public String getName() {
+ return "WRT Widget";
+ }
+
+ public void initializeFrom(ILaunchConfiguration configuration) {
+ project.setSelection(getSelectedProject(configuration), true);
+ validate();
+ setErrorMessage(null);
+ }
+
+ private ISelection getSelectedProject(ILaunchConfiguration configuration) {
+ ISelection selected = StructuredSelection.EMPTY;
+ try {
+ String projectName = configuration.getAttribute(
+ IConstants.PROP_PROJECT_NAME, (String) null);
+ if (projectName != null) {
+ IProject p = ResourcesPlugin.getWorkspace().getRoot()
+ .getProject(projectName);
+ if (p.isAccessible()) {
+ selected = new StructuredSelection(p);
+ }
+ }
+ } catch (CoreException e) {
+ Activator.log(e);
+ }
+ return selected;
+ }
+
+ @Override
+ public boolean canSave() {
+ return canSave;
+ }
+
+ private void validate() {
+ String error = null;
+ if (getWidgetProjects().length == 0) {
+ error = "No WRT widget projects found in the workspace";
+ } else if (project.getSelection().isEmpty()) {
+ error = "Select WRT widget project to debug";
+ } else if (ChromeDebugUtils.getChromeExecutible() == null) {
+ error = "No Chrome browser configured in the preferences";
+ }
+ canSave = error == null;
+ setErrorMessage(error);
+ getLaunchConfigurationDialog().updateButtons();
+ }
+
+ public void performApply(ILaunchConfigurationWorkingCopy configuration) {
+ ISelection selection = project.getSelection();
+ if (selection.isEmpty()) {
+ setDefaults(configuration);
+ } else {
+ IProject p = (IProject) ((IStructuredSelection) selection)
+ .getFirstElement();
+ configuration.setAttribute(IConstants.PROP_PROJECT_NAME, p
+ .getName());
+ configuration.setMappedResources(new IResource[] { p });
+ }
+ }
+
+ public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
+ configuration.removeAttribute(IConstants.PROP_PROJECT_NAME);
+ }
+
+ @Override
+ public boolean isValid(ILaunchConfiguration launchConfig) {
+ return !getSelectedProject(launchConfig).isEmpty();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/ui/launch/WidgetLaunchConfigurationTabGroup.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.ui.launch;
+
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
+import org.eclipse.debug.ui.CommonTab;
+import org.eclipse.debug.ui.ILaunchConfigurationDialog;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+
+public class WidgetLaunchConfigurationTabGroup extends
+ AbstractLaunchConfigurationTabGroup {
+
+ public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
+ setTabs(new ILaunchConfigurationTab[] {new WidgetBasicTab(), new CommonTab()});
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/ui/launch/WidgetLaunchShortcut.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Symbian Foundation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Symbian Foundation - initial contribution.
+ * Contributors:
+ * Description:
+ * Overview:
+ * Details:
+ * Platforms/Drives/Compatibility:
+ * Assumptions/Requirement/Pre-requisites:
+ * Failures and causes:
+ *******************************************************************************/
+package org.symbian.tools.wrttools.debug.ui.launch;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationType;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.debug.ui.ILaunchShortcut2;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.symbian.tools.wrttools.debug.internal.Activator;
+import org.symbian.tools.wrttools.debug.internal.IConstants;
+import org.symbian.tools.wrttools.debug.internal.launch.WidgetLaunchDelegate;
+
+public class WidgetLaunchShortcut implements ILaunchShortcut2 {
+
+ public IResource getLaunchableResource(IEditorPart editorpart) {
+ IEditorInput input = editorpart.getEditorInput();
+ return (IResource) input.getAdapter(IResource.class);
+ }
+
+ public IResource getLaunchableResource(ISelection selection) {
+ if (!selection.isEmpty() && selection instanceof IStructuredSelection) {
+ Object object = ((IStructuredSelection) selection)
+ .getFirstElement();
+ IResource resource = null;
+ if (object instanceof IResource) {
+ resource = (IResource) object;
+ } else if (object instanceof IAdaptable) {
+ resource = (IResource) ((IAdaptable) object)
+ .getAdapter(IResource.class);
+ }
+ return resource;
+ }
+ return null;
+ }
+
+ public ILaunchConfiguration[] getLaunchConfigurations(IEditorPart editorpart) {
+ return getLaunchConfigurations(getLaunchableResource(editorpart));
+ }
+
+ private ILaunchConfiguration getLaunchConfigurations(IProject project)
+ throws CoreException {
+ ILaunchManager launchManager = DebugPlugin.getDefault()
+ .getLaunchManager();
+ ILaunchConfigurationType type = launchManager
+ .getLaunchConfigurationType(WidgetLaunchDelegate.ID);
+ ILaunchConfiguration configuration = null;
+ ILaunchConfiguration[] configurations = launchManager
+ .getLaunchConfigurations(type);
+ for (ILaunchConfiguration c : configurations) {
+ if (project.getName()
+ .equals(
+ c.getAttribute(IConstants.PROP_PROJECT_NAME,
+ (String) null))) {
+ configuration = c;
+ break;
+ }
+ }
+ return configuration;
+ }
+
+ private ILaunchConfiguration[] getLaunchConfigurations(IResource resource) {
+ if (resource != null) {
+ try {
+ ILaunchConfiguration launchConfigurations = getLaunchConfigurations(resource
+ .getProject());
+ if (launchConfigurations != null) {
+ return new ILaunchConfiguration[] { launchConfigurations };
+ }
+ } catch (CoreException e) {
+ Activator.log(e);
+ }
+ }
+ return null;
+ }
+
+ public ILaunchConfiguration[] getLaunchConfigurations(ISelection selection) {
+ return getLaunchConfigurations(getLaunchableResource(selection));
+ }
+
+ public void launch(IEditorPart editor, String mode) {
+ launch(getLaunchableResource(editor), mode);
+ }
+
+ private void launch(IResource launchableResource, String mode) {
+ try {
+ IProject project = launchableResource.getProject();
+ ILaunchManager launchManager = DebugPlugin.getDefault()
+ .getLaunchManager();
+ ILaunchConfigurationType type = launchManager
+ .getLaunchConfigurationType(WidgetLaunchDelegate.ID);
+ ILaunchConfiguration configuration = getLaunchConfigurations(project);
+ if (configuration == null) {
+ ILaunchConfigurationWorkingCopy copy = type
+ .newInstance(
+ null,
+ launchManager
+ .generateUniqueLaunchConfigurationNameFrom(project
+ .getName()));
+ copy.setAttribute(IConstants.PROP_PROJECT_NAME, project
+ .getName());
+ copy.setMappedResources(new IResource[] {project});
+ configuration = copy.doSave();
+ }
+ DebugUITools.launch(configuration, mode);
+ } catch (CoreException e) {
+ Activator.log(e);
+ }
+ }
+
+ public void launch(ISelection selection, String mode) {
+ launch(getLaunchableResource(selection), mode);
+ }
+}