Initial version of WRT Debugger.
authorTasneemS@US-TASNEEMS
Wed, 23 Dec 2009 17:13:18 -0800
changeset 2 e4420d2515f1
parent 1 ef76fc2ac88c
child 3 d3477de62514
child 4 e559db44c84a
Initial version of WRT Debugger.
org.chromium.debug.core/.classpath
org.chromium.debug.core/.options
org.chromium.debug.core/.project
org.chromium.debug.core/LICENSE
org.chromium.debug.core/META-INF/MANIFEST.MF
org.chromium.debug.core/bin/org/chromium/debug/core/ChromiumDebugPlugin.class
org.chromium.debug.core/bin/org/chromium/debug/core/Messages.class
org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptFileStore.class
org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptFileSystem.class
org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptStorage$CommonNode.class
org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptStorage$FileNode$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptStorage$FileNode.class
org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptStorage$FolderNode.class
org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptStorage$RootNode.class
org.chromium.debug.core/bin/org/chromium/debug/core/efs/ChromiumScriptStorage.class
org.chromium.debug.core/bin/org/chromium/debug/core/messages.properties
org.chromium.debug.core/bin/org/chromium/debug/core/model/ArrayValue.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/BreakpointAdapterFactory.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/BreakpointRegistry$BreakpointEntry.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/BreakpointRegistry$BreakpointLocation.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/BreakpointRegistry$ScriptIdentifier.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/BreakpointRegistry.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/ChromiumBreakpointWBAFactory$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/ChromiumBreakpointWBAFactory.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/ChromiumLineBreakpoint$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/ChromiumLineBreakpoint.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/ConnectionLoggerImpl$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/ConnectionLoggerImpl$2.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/ConnectionLoggerImpl$3.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/ConnectionLoggerImpl$LogLifecycleListener.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/ConnectionLoggerImpl.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/ConsolePseudoProcess$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/ConsolePseudoProcess$NullStreamMonitor.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/ConsolePseudoProcess$Retransmitter.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/ConsolePseudoProcess.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugElementImpl.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$2.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$3.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$4.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$5.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$6$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$6.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$7$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$7.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$8.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl$DebugEventListenerImpl.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/DebugTargetImpl.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/Destructable.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/DestructingGuard.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/IChromiumDebugTarget.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/IResourceManager.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptThread.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedder$ConnectionToRemote.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedder$Listener.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedder$VmConnector.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedder.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$2$1$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$2$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$2.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$BrowserCache$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$BrowserCache.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$EmbeddingTabConnector$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$EmbeddingTabConnector$2.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory$EmbeddingTabConnector.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/JavascriptVmEmbedderFactory.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/LineBreakpointAdapter.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/Messages.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/NamedConnectionLoggerFactory.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/ResourceManager.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/StackFrame$1ScopeObjectVariable.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/StackFrame.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/TabSelector.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/Value.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/Variable$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/Variable.class
org.chromium.debug.core/bin/org/chromium/debug/core/model/messages.properties
org.chromium.debug.core/bin/org/chromium/debug/core/util/ChromiumDebugPluginUtil$1.class
org.chromium.debug.core/bin/org/chromium/debug/core/util/ChromiumDebugPluginUtil$2.class
org.chromium.debug.core/bin/org/chromium/debug/core/util/ChromiumDebugPluginUtil.class
org.chromium.debug.core/bin/org/chromium/debug/core/util/JsValueStringifier$Config.class
org.chromium.debug.core/bin/org/chromium/debug/core/util/JsValueStringifier.class
org.chromium.debug.core/build.properties
org.chromium.debug.core/plugin.properties
org.chromium.debug.core/plugin.xml
org.chromium.debug.core/src/org/chromium/debug/core/ChromiumDebugPlugin.java
org.chromium.debug.core/src/org/chromium/debug/core/Messages.java
org.chromium.debug.core/src/org/chromium/debug/core/efs/ChromiumScriptFileStore.java
org.chromium.debug.core/src/org/chromium/debug/core/efs/ChromiumScriptFileSystem.java
org.chromium.debug.core/src/org/chromium/debug/core/efs/ChromiumScriptStorage.java
org.chromium.debug.core/src/org/chromium/debug/core/messages.properties
org.chromium.debug.core/src/org/chromium/debug/core/model/ArrayValue.java
org.chromium.debug.core/src/org/chromium/debug/core/model/BreakpointAdapterFactory.java
org.chromium.debug.core/src/org/chromium/debug/core/model/BreakpointRegistry.java
org.chromium.debug.core/src/org/chromium/debug/core/model/ChromiumBreakpointWBAFactory.java
org.chromium.debug.core/src/org/chromium/debug/core/model/ChromiumLineBreakpoint.java
org.chromium.debug.core/src/org/chromium/debug/core/model/ConnectionLoggerImpl.java
org.chromium.debug.core/src/org/chromium/debug/core/model/ConsolePseudoProcess.java
org.chromium.debug.core/src/org/chromium/debug/core/model/DebugElementImpl.java
org.chromium.debug.core/src/org/chromium/debug/core/model/DebugTargetImpl.java
org.chromium.debug.core/src/org/chromium/debug/core/model/Destructable.java
org.chromium.debug.core/src/org/chromium/debug/core/model/DestructingGuard.java
org.chromium.debug.core/src/org/chromium/debug/core/model/IChromiumDebugTarget.java
org.chromium.debug.core/src/org/chromium/debug/core/model/IResourceManager.java
org.chromium.debug.core/src/org/chromium/debug/core/model/JavascriptThread.java
org.chromium.debug.core/src/org/chromium/debug/core/model/JavascriptVmEmbedder.java
org.chromium.debug.core/src/org/chromium/debug/core/model/JavascriptVmEmbedderFactory.java
org.chromium.debug.core/src/org/chromium/debug/core/model/LineBreakpointAdapter.java
org.chromium.debug.core/src/org/chromium/debug/core/model/Messages.java
org.chromium.debug.core/src/org/chromium/debug/core/model/NamedConnectionLoggerFactory.java
org.chromium.debug.core/src/org/chromium/debug/core/model/ResourceManager.java
org.chromium.debug.core/src/org/chromium/debug/core/model/StackFrame.java
org.chromium.debug.core/src/org/chromium/debug/core/model/TabSelector.java
org.chromium.debug.core/src/org/chromium/debug/core/model/Value.java
org.chromium.debug.core/src/org/chromium/debug/core/model/Variable.java
org.chromium.debug.core/src/org/chromium/debug/core/model/messages.properties
org.chromium.debug.core/src/org/chromium/debug/core/util/ChromiumDebugPluginUtil.java
org.chromium.debug.core/src/org/chromium/debug/core/util/JsValueStringifier.java
org.chromium.debug.ui/.classpath
org.chromium.debug.ui/.project
org.chromium.debug.ui/LICENSE
org.chromium.debug.ui/META-INF/MANIFEST.MF
org.chromium.debug.ui/bin/org/chromium/debug/ui/ChromiumDebugUIPlugin.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/ChromiumJavascriptDecorator.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/ChromiumTabSelectionDialog$1.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/ChromiumTabSelectionDialog$2.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/ChromiumTabSelectionDialog.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/DialogBasedTabSelector$1.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/DialogBasedTabSelector.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/JsDebugModelPresentation.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/JsEvalContextManager$1.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/JsEvalContextManager.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/JsWatchExpressionDelegate$1.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/JsWatchExpressionDelegate$2.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/JsWatchExpressionDelegate$BadWatchExpressionResult.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/JsWatchExpressionDelegate$GoodWatchExpressionResult.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/JsWatchExpressionDelegate.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/Messages.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/PluginUtil$1.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/PluginUtil.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/JsBreakpointPropertiesRulerAction$1.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/JsBreakpointPropertiesRulerAction.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/JsBreakpointPropertiesRulerActionDelegate.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/JsInspectExpression.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/JsInspectSnippetAction$1.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/JsInspectSnippetAction$2.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/JsInspectSnippetAction.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/Messages.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/OpenFunctionAction$1.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/OpenFunctionAction.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/actions/messages.properties
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/EditorColors.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JavascriptUtil.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsCodeScanner$1.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsCodeScanner$KeywordRule.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsCodeScanner$WordDetector.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsCodeScanner.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsDebugTextHover$1.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsDebugTextHover.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsDocumentProvider.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsEditor.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsPartitionScanner$EmptyCommentDetector.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsPartitionScanner$EmptyCommentPredicateRule.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsPartitionScanner.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsSourceViewerConfiguration$MultilineCommentScanner.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/editors/JsSourceViewerConfiguration.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/ChromiumLaunchType$ConnectionLoggerFactoryImpl.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/ChromiumLaunchType.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/ChromiumRemoteTab$1.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/ChromiumRemoteTab.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTabGroup$Chromium.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTabGroup$StandaloneV8.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTabGroup.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTypeBase$1.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTypeBase$2.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTypeBase$3.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTypeBase$4.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTypeBase$5.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/LaunchTypeBase.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/Messages.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/PluginVariablesUtil.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/StandaloneV8LaunchType$1.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/StandaloneV8LaunchType.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/launcher/messages.properties
org.chromium.debug.ui/bin/org/chromium/debug/ui/messages.properties
org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/JsLineBreakpointPage$1.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/JsLineBreakpointPage$2.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/JsLineBreakpointPage$3.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/JsLineBreakpointPage$4.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/JsLineBreakpointPage$5.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/JsLineBreakpointPage.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/Messages.class
org.chromium.debug.ui/bin/org/chromium/debug/ui/propertypages/messages.properties
org.chromium.debug.ui/build.properties
org.chromium.debug.ui/plugin.properties
org.chromium.debug.ui/plugin.xml
org.chromium.debug.ui/res/chromium_16.png
org.chromium.debug.ui/res/standalone_v8_16.png
org.chromium.debug.ui/src/org/chromium/debug/ui/ChromiumDebugUIPlugin.java
org.chromium.debug.ui/src/org/chromium/debug/ui/ChromiumJavascriptDecorator.java
org.chromium.debug.ui/src/org/chromium/debug/ui/ChromiumTabSelectionDialog.java
org.chromium.debug.ui/src/org/chromium/debug/ui/DialogBasedTabSelector.java
org.chromium.debug.ui/src/org/chromium/debug/ui/JsDebugModelPresentation.java
org.chromium.debug.ui/src/org/chromium/debug/ui/JsEvalContextManager.java
org.chromium.debug.ui/src/org/chromium/debug/ui/JsWatchExpressionDelegate.java
org.chromium.debug.ui/src/org/chromium/debug/ui/Messages.java
org.chromium.debug.ui/src/org/chromium/debug/ui/PluginUtil.java
org.chromium.debug.ui/src/org/chromium/debug/ui/actions/JsBreakpointPropertiesRulerAction.java
org.chromium.debug.ui/src/org/chromium/debug/ui/actions/JsBreakpointPropertiesRulerActionDelegate.java
org.chromium.debug.ui/src/org/chromium/debug/ui/actions/JsInspectExpression.java
org.chromium.debug.ui/src/org/chromium/debug/ui/actions/JsInspectSnippetAction.java
org.chromium.debug.ui/src/org/chromium/debug/ui/actions/Messages.java
org.chromium.debug.ui/src/org/chromium/debug/ui/actions/OpenFunctionAction.java
org.chromium.debug.ui/src/org/chromium/debug/ui/actions/messages.properties
org.chromium.debug.ui/src/org/chromium/debug/ui/editors/EditorColors.java
org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JavascriptUtil.java
org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JsCodeScanner.java
org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JsDebugTextHover.java
org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JsDocumentProvider.java
org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JsEditor.java
org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JsPartitionScanner.java
org.chromium.debug.ui/src/org/chromium/debug/ui/editors/JsSourceViewerConfiguration.java
org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/ChromiumLaunchType.java
org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/ChromiumRemoteTab.java
org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/LaunchTabGroup.java
org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/LaunchTypeBase.java
org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/Messages.java
org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/PluginVariablesUtil.java
org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/StandaloneV8LaunchType.java
org.chromium.debug.ui/src/org/chromium/debug/ui/launcher/messages.properties
org.chromium.debug.ui/src/org/chromium/debug/ui/messages.properties
org.chromium.debug.ui/src/org/chromium/debug/ui/propertypages/JsLineBreakpointPage.java
org.chromium.debug.ui/src/org/chromium/debug/ui/propertypages/Messages.java
org.chromium.debug.ui/src/org/chromium/debug/ui/propertypages/messages.properties
org.chromium.sdk/.classpath
org.chromium.sdk/.project
org.chromium.sdk/LICENSE
org.chromium.sdk/META-INF/MANIFEST.MF
org.chromium.sdk/bin/org/chromium/sdk/Breakpoint$Type.class
org.chromium.sdk/bin/org/chromium/sdk/Breakpoint.class
org.chromium.sdk/bin/org/chromium/sdk/Browser$TabConnector.class
org.chromium.sdk/bin/org/chromium/sdk/Browser$TabFetcher.class
org.chromium.sdk/bin/org/chromium/sdk/Browser.class
org.chromium.sdk/bin/org/chromium/sdk/BrowserFactory.class
org.chromium.sdk/bin/org/chromium/sdk/BrowserTab.class
org.chromium.sdk/bin/org/chromium/sdk/CallFrame$EvaluateCallback.class
org.chromium.sdk/bin/org/chromium/sdk/CallFrame.class
org.chromium.sdk/bin/org/chromium/sdk/CallbackSemaphore.class
org.chromium.sdk/bin/org/chromium/sdk/ConnectionLogger$ConnectionCloser.class
org.chromium.sdk/bin/org/chromium/sdk/ConnectionLogger$Factory.class
org.chromium.sdk/bin/org/chromium/sdk/ConnectionLogger.class
org.chromium.sdk/bin/org/chromium/sdk/DebugContext$ContinueCallback.class
org.chromium.sdk/bin/org/chromium/sdk/DebugContext$State.class
org.chromium.sdk/bin/org/chromium/sdk/DebugContext$StepAction.class
org.chromium.sdk/bin/org/chromium/sdk/DebugContext.class
org.chromium.sdk/bin/org/chromium/sdk/DebugEventListener.class
org.chromium.sdk/bin/org/chromium/sdk/ExceptionData.class
org.chromium.sdk/bin/org/chromium/sdk/InvalidContextException.class
org.chromium.sdk/bin/org/chromium/sdk/JavascriptVm$BreakpointCallback.class
org.chromium.sdk/bin/org/chromium/sdk/JavascriptVm$ScriptsCallback.class
org.chromium.sdk/bin/org/chromium/sdk/JavascriptVm$SuspendCallback.class
org.chromium.sdk/bin/org/chromium/sdk/JavascriptVm.class
org.chromium.sdk/bin/org/chromium/sdk/JsArray.class
org.chromium.sdk/bin/org/chromium/sdk/JsFunction.class
org.chromium.sdk/bin/org/chromium/sdk/JsObject.class
org.chromium.sdk/bin/org/chromium/sdk/JsScope$Type.class
org.chromium.sdk/bin/org/chromium/sdk/JsScope.class
org.chromium.sdk/bin/org/chromium/sdk/JsValue$Type.class
org.chromium.sdk/bin/org/chromium/sdk/JsValue.class
org.chromium.sdk/bin/org/chromium/sdk/JsVariable$SetValueCallback.class
org.chromium.sdk/bin/org/chromium/sdk/JsVariable.class
org.chromium.sdk/bin/org/chromium/sdk/Script$Type.class
org.chromium.sdk/bin/org/chromium/sdk/Script.class
org.chromium.sdk/bin/org/chromium/sdk/StandaloneVm.class
org.chromium.sdk/bin/org/chromium/sdk/SyncCallback.class
org.chromium.sdk/bin/org/chromium/sdk/TabDebugEventListener.class
org.chromium.sdk/bin/org/chromium/sdk/UnsupportedVersionException.class
org.chromium.sdk/bin/org/chromium/sdk/Version.class
org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserFactoryImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$ConnectionSessionManager.class
org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$ExceptionWrapper$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$ExceptionWrapper$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$ExceptionWrapper.class
org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$Session$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$Session$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$Session.class
org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$TabConnectorImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl$TabFetcherImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserTabImpl$ChromeDevToolOutput.class
org.chromium.sdk/bin/org/chromium/sdk/internal/BrowserTabImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/CallFrameImpl$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/CallFrameImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/CloseableMap$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/CloseableMap.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ConnectionFactory.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$1$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$DebugContextData.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$ExpectingBacktraceStep.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$ExpectingBreakEventStep.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$Frames.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$PreContext$UserContext$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$PreContext$UserContext.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder$PreContext.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ContextBuilder.class
org.chromium.sdk/bin/org/chromium/sdk/internal/DataWithRef$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/DataWithRef$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/DataWithRef.class
org.chromium.sdk/bin/org/chromium/sdk/internal/DebugSession$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/DebugSession$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/DebugSession$ScriptLoader$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/DebugSession$ScriptLoader.class
org.chromium.sdk/bin/org/chromium/sdk/internal/DebugSession.class
org.chromium.sdk/bin/org/chromium/sdk/internal/DebugSessionManager.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ExceptionDataImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/FrameMirror.class
org.chromium.sdk/bin/org/chromium/sdk/internal/FunctionAdditionalProperties.class
org.chromium.sdk/bin/org/chromium/sdk/internal/HandleManager.class
org.chromium.sdk/bin/org/chromium/sdk/internal/InternalContext$ContextDismissedCheckedException.class
org.chromium.sdk/bin/org/chromium/sdk/internal/InternalContext.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JavascriptVmImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsArrayImpl$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsArrayImpl$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsArrayImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsDataTypeUtil.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsFunctionImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsObjectImpl$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsObjectImpl$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsObjectImpl$Subproperties.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsObjectImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsScopeImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsValueImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsVariableImpl$NameDecorator$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsVariableImpl$NameDecorator.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsVariableImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsonException.class
org.chromium.sdk/bin/org/chromium/sdk/internal/JsonUtil.class
org.chromium.sdk/bin/org/chromium/sdk/internal/MessageFactory.class
org.chromium.sdk/bin/org/chromium/sdk/internal/PropertyHoldingValueMirror.class
org.chromium.sdk/bin/org/chromium/sdk/internal/PropertyReference.class
org.chromium.sdk/bin/org/chromium/sdk/internal/PropertyType.class
org.chromium.sdk/bin/org/chromium/sdk/internal/Result.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ScopeMirror.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ScriptImpl$Descriptor.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ScriptImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ScriptManager$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ScriptManager$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ScriptManager$Callback.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ScriptManager.class
org.chromium.sdk/bin/org/chromium/sdk/internal/SessionManager$SessionBase$TicketImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/SessionManager$SessionBase.class
org.chromium.sdk/bin/org/chromium/sdk/internal/SessionManager$Ticket.class
org.chromium.sdk/bin/org/chromium/sdk/internal/SessionManager.class
org.chromium.sdk/bin/org/chromium/sdk/internal/SocketConnectionFactory.class
org.chromium.sdk/bin/org/chromium/sdk/internal/StandaloneVmImpl$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/StandaloneVmImpl$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/StandaloneVmImpl$3.class
org.chromium.sdk/bin/org/chromium/sdk/internal/StandaloneVmImpl$4.class
org.chromium.sdk/bin/org/chromium/sdk/internal/StandaloneVmImpl$ConnectionState.class
org.chromium.sdk/bin/org/chromium/sdk/internal/StandaloneVmImpl$V8CommandOutputImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/StandaloneVmImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror$FunctionValueBased.class
org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror$JsonBased$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror$JsonBased$AdditionalPropertyFactory.class
org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror$JsonBased.class
org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror$ListBased.class
org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror$ObjectValueBased.class
org.chromium.sdk/bin/org/chromium/sdk/internal/SubpropertiesMirror.class
org.chromium.sdk/bin/org/chromium/sdk/internal/V8ContextFilter.class
org.chromium.sdk/bin/org/chromium/sdk/internal/V8VersionMilestones.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ValueLoadException.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ValueLoader$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ValueLoader$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ValueLoader.class
org.chromium.sdk/bin/org/chromium/sdk/internal/ValueMirror.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/AfterCompileBody.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/BacktraceCommandBody.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/BreakEventBody.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/BreakpointBody.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/CommandResponse$TypeValueCondition.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/CommandResponse.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/CommandResponseBody.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/EventNotification$TypeValueCondition.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/EventNotification.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/EventNotificationBody.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/FailedCommandResponse.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/FrameObject.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/IncomingMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/MessageType.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/ScopeBody.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/ScopeRef.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/SuccessCommandResponse.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/VersionBody.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/ContextData.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/ContextHandle.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/FunctionValueHandle.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/ObjectValueHandle.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/PropertyObject.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/PropertyWithRef.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/PropertyWithValue.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/RefWithDisplayData.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/ScriptHandle.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/SomeHandle.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/SomeRef.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/SomeSerialized.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocol/data/ValueHandle.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/AnyObjectBased.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/EnumValueCondition.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonField.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonNullable.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonObjectBased.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonOptionalField.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonOverrideField.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonProtocolModelParseException.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonProtocolParseException.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonSubtype.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonSubtypeCasting.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonSubtypeCondition.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonSubtypeConditionBoolValue.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonSubtypeConditionCustom.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonType.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/JsonValueCondition.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/BaseHandlersLibrary$GetAnyObjectMethodHaldler.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/BaseHandlersLibrary$GetJsonObjectMethodHaldler.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/BaseHandlersLibrary$GetSuperMethodHaldler.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/BaseHandlersLibrary$MethodHandlerBase.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/BaseHandlersLibrary$SelfCallMethodHanlder.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/BaseHandlersLibrary.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/EnumParser.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldCondition.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic$3.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic$4.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic$5.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic$CustomConditionWrapper.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldLoadedFinisher.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldLoader.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonInvocationHandler.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$AlgebraicCasesDataImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$ArrayParser.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$AutoSubtypeMethodHandler.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$EagerFieldParserImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$FieldMap.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$JsonProtocolParseRuntimeException.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$LazyParseFieldMethodHandler.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$ManualSubtypeMethodHandler.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$PreparsedFieldMethodHandler.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$ReadInterfacesSession$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$ReadInterfacesSession$FieldProcessor$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$ReadInterfacesSession$FieldProcessor$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$ReadInterfacesSession$FieldProcessor.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$ReadInterfacesSession.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$RefImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$SimpleCastParser.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser$SimpleParserPair.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonTypeParser$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonTypeParser.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/MethodHandler.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/ObjectData.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/QuickParser.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/RefToType.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/SlowParser.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/SubtypeCaster.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler$AbsentSubtypeAspect.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler$AlgebraicCasesData.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler$EagerFieldParser.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler$ExistingSubtypeAspect.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler$SubtypeAspect.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler$SubtypeSupport.class
org.chromium.sdk/bin/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/ChromeDevToolsProtocol.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/ToolHandler.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/ToolName.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/ToolOutput.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceCommand.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler$CommandFactory.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler$ListTabsCallback.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler$TabIdAndUrl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler$VersionCallback.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/BreakpointImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/BreakpointManager$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/BreakpointManager$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/BreakpointManager$3.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/BreakpointManager.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$AttachState.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$AttachmentFailureException.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$ResultAwareCallback.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$ToolHandlerImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$V8CommandOutputImpl.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager$V8DebuggerToolMessageFactory.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/DebuggerCommand.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/DebuggerToolCommand.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/DefaultResponseHandler$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/DefaultResponseHandler$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/DefaultResponseHandler$ProcessorGetter.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/DefaultResponseHandler.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/MethodIsBlockingException.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8BlockingCallback.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandOutput.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandProcessor$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandProcessor$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandProcessor$CallbackCaller.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandProcessor$CallbackEntry.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandProcessor$V8HandlerCallback$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandProcessor$V8HandlerCallback.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandProcessor.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8CommandSender.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8Helper$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8Helper$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8Helper$3.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8Helper$CallbackException.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8Helper.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8Protocol.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil$PropertyNameGetter$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil$PropertyNameGetter$SimpleNameGetter.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil$PropertyNameGetter$SubpropertyNameGetter.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil$PropertyNameGetter.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/processor/AfterCompileProcessor$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/processor/AfterCompileProcessor.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/processor/BacktraceProcessor$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/processor/BacktraceProcessor.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/processor/BreakpointProcessor.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/processor/V8EventProcessor.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/BacktraceMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/ChangeBreakpointMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/ClearBreakpointMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/ContextlessDebuggerMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/ContinueMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/DebuggerMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/DebuggerMessageFactory.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/EvaluateMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/FrameMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/LookupMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/ScopeMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/ScriptsMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/SeqGenerator.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/SetBreakpointMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/SourceMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/SuspendMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/V8MessageType.class
org.chromium.sdk/bin/org/chromium/sdk/internal/tools/v8/request/VersionMessage.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Connection$NetListener.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Connection.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Handshaker$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Handshaker$StandaloneV8$HandshakeTaks$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Handshaker$StandaloneV8$HandshakeTaks.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Handshaker$StandaloneV8$RemoteInfo.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Handshaker$StandaloneV8.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Handshaker.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Message$Header.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Message$MalformedMessageException.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/Message.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$1.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$2.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$3.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$4.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$InterruptibleThread.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$MessageItem.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$ReaderThread.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$RegularMessageItem.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$ResponseDispatcherThread.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection$WriterThread.class
org.chromium.sdk/bin/org/chromium/sdk/internal/transport/SocketConnection.class
org.chromium.sdk/build.properties
org.chromium.sdk/lib/json_simple/AUTHORS.txt
org.chromium.sdk/lib/json_simple/LICENSE.txt
org.chromium.sdk/lib/json_simple/README.txt
org.chromium.sdk/lib/json_simple/json_simple-1.1.jar
org.chromium.sdk/src/org/chromium/sdk/Breakpoint.java
org.chromium.sdk/src/org/chromium/sdk/Browser.java
org.chromium.sdk/src/org/chromium/sdk/BrowserFactory.java
org.chromium.sdk/src/org/chromium/sdk/BrowserTab.java
org.chromium.sdk/src/org/chromium/sdk/CallFrame.java
org.chromium.sdk/src/org/chromium/sdk/CallbackSemaphore.java
org.chromium.sdk/src/org/chromium/sdk/ConnectionLogger.java
org.chromium.sdk/src/org/chromium/sdk/DebugContext.java
org.chromium.sdk/src/org/chromium/sdk/DebugEventListener.java
org.chromium.sdk/src/org/chromium/sdk/ExceptionData.java
org.chromium.sdk/src/org/chromium/sdk/InvalidContextException.java
org.chromium.sdk/src/org/chromium/sdk/JavascriptVm.java
org.chromium.sdk/src/org/chromium/sdk/JsArray.java
org.chromium.sdk/src/org/chromium/sdk/JsFunction.java
org.chromium.sdk/src/org/chromium/sdk/JsObject.java
org.chromium.sdk/src/org/chromium/sdk/JsScope.java
org.chromium.sdk/src/org/chromium/sdk/JsValue.java
org.chromium.sdk/src/org/chromium/sdk/JsVariable.java
org.chromium.sdk/src/org/chromium/sdk/Script.java
org.chromium.sdk/src/org/chromium/sdk/StandaloneVm.java
org.chromium.sdk/src/org/chromium/sdk/SyncCallback.java
org.chromium.sdk/src/org/chromium/sdk/TabDebugEventListener.java
org.chromium.sdk/src/org/chromium/sdk/UnsupportedVersionException.java
org.chromium.sdk/src/org/chromium/sdk/Version.java
org.chromium.sdk/src/org/chromium/sdk/internal/BrowserFactoryImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/BrowserImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/BrowserTabImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/CallFrameImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/CloseableMap.java
org.chromium.sdk/src/org/chromium/sdk/internal/ConnectionFactory.java
org.chromium.sdk/src/org/chromium/sdk/internal/ContextBuilder.java
org.chromium.sdk/src/org/chromium/sdk/internal/DataWithRef.java
org.chromium.sdk/src/org/chromium/sdk/internal/DebugSession.java
org.chromium.sdk/src/org/chromium/sdk/internal/DebugSessionManager.java
org.chromium.sdk/src/org/chromium/sdk/internal/ExceptionDataImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/FrameMirror.java
org.chromium.sdk/src/org/chromium/sdk/internal/FunctionAdditionalProperties.java
org.chromium.sdk/src/org/chromium/sdk/internal/HandleManager.java
org.chromium.sdk/src/org/chromium/sdk/internal/InternalContext.java
org.chromium.sdk/src/org/chromium/sdk/internal/JavascriptVmImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/JsArrayImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/JsDataTypeUtil.java
org.chromium.sdk/src/org/chromium/sdk/internal/JsFunctionImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/JsObjectImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/JsScopeImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/JsValueImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/JsVariableImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/JsonException.java
org.chromium.sdk/src/org/chromium/sdk/internal/JsonUtil.java
org.chromium.sdk/src/org/chromium/sdk/internal/MessageFactory.java
org.chromium.sdk/src/org/chromium/sdk/internal/PropertyHoldingValueMirror.java
org.chromium.sdk/src/org/chromium/sdk/internal/PropertyReference.java
org.chromium.sdk/src/org/chromium/sdk/internal/PropertyType.java
org.chromium.sdk/src/org/chromium/sdk/internal/Result.java
org.chromium.sdk/src/org/chromium/sdk/internal/ScopeMirror.java
org.chromium.sdk/src/org/chromium/sdk/internal/ScriptImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/ScriptManager.java
org.chromium.sdk/src/org/chromium/sdk/internal/SessionManager.java
org.chromium.sdk/src/org/chromium/sdk/internal/SocketConnectionFactory.java
org.chromium.sdk/src/org/chromium/sdk/internal/StandaloneVmImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/SubpropertiesMirror.java
org.chromium.sdk/src/org/chromium/sdk/internal/V8ContextFilter.java
org.chromium.sdk/src/org/chromium/sdk/internal/V8VersionMilestones.java
org.chromium.sdk/src/org/chromium/sdk/internal/ValueLoadException.java
org.chromium.sdk/src/org/chromium/sdk/internal/ValueLoader.java
org.chromium.sdk/src/org/chromium/sdk/internal/ValueMirror.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/AfterCompileBody.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/BacktraceCommandBody.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/BreakEventBody.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/BreakpointBody.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/CommandResponse.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/CommandResponseBody.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/EventNotification.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/EventNotificationBody.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/FailedCommandResponse.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/FrameObject.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/IncomingMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/MessageType.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/ScopeBody.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/ScopeRef.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/SuccessCommandResponse.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/VersionBody.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/ContextData.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/ContextHandle.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/FunctionValueHandle.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/ObjectValueHandle.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/PropertyObject.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/PropertyWithRef.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/PropertyWithValue.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/RefWithDisplayData.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/ScriptHandle.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/SomeHandle.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/SomeRef.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/SomeSerialized.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocol/data/ValueHandle.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/AnyObjectBased.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/EnumValueCondition.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonField.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonNullable.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonObjectBased.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonOptionalField.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonOverrideField.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonProtocolModelParseException.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonProtocolParseException.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonSubtype.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonSubtypeCasting.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonSubtypeCondition.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonSubtypeConditionBoolValue.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonSubtypeConditionCustom.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonType.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/JsonValueCondition.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/BaseHandlersLibrary.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/EnumParser.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldCondition.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldConditionLogic.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldLoadedFinisher.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/FieldLoader.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonInvocationHandler.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonProtocolParser.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/JsonTypeParser.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/MethodHandler.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/ObjectData.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/QuickParser.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/RefToType.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/SlowParser.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/SubtypeCaster.java
org.chromium.sdk/src/org/chromium/sdk/internal/protocolparser/dynamicimpl/TypeHandler.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/ChromeDevToolsProtocol.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/ToolHandler.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/ToolName.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/ToolOutput.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/devtools/DevToolsServiceCommand.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/devtools/DevToolsServiceHandler.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/BreakpointImpl.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/BreakpointManager.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/ChromeDevToolSessionManager.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/DebuggerCommand.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/DebuggerToolCommand.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/DefaultResponseHandler.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/MethodIsBlockingException.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8BlockingCallback.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8CommandOutput.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8CommandProcessor.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8CommandSender.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8Helper.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8Protocol.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8ProtocolUtil.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/processor/AfterCompileProcessor.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/processor/BacktraceProcessor.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/processor/BreakpointProcessor.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/processor/V8EventProcessor.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/BacktraceMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/ChangeBreakpointMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/ClearBreakpointMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/ContextlessDebuggerMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/ContinueMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/DebuggerMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/DebuggerMessageFactory.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/EvaluateMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/FrameMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/LookupMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/ScopeMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/ScriptsMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/SeqGenerator.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/SetBreakpointMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/SourceMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/SuspendMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/V8MessageType.java
org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/request/VersionMessage.java
org.chromium.sdk/src/org/chromium/sdk/internal/transport/Connection.java
org.chromium.sdk/src/org/chromium/sdk/internal/transport/Handshaker.java
org.chromium.sdk/src/org/chromium/sdk/internal/transport/Message.java
org.chromium.sdk/src/org/chromium/sdk/internal/transport/SocketConnection.java
org.symbian.tools.wrttools.debug.core/.classpath
org.symbian.tools.wrttools.debug.core/.project
org.symbian.tools.wrttools.debug.core/.settings/org.eclipse.jdt.core.prefs
org.symbian.tools.wrttools.debug.core/LICENSE
org.symbian.tools.wrttools.debug.core/META-INF/MANIFEST.MF
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/Activator.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/BreakpointAdapterFactory.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/ChromeDebugUtils.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/IConstants.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/Images.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/PreferenceInitializer.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/WorkspaceLineBreakpointAdapter.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/launch/DebugConnectionJob$1.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/launch/DebugConnectionJob$2.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/launch/DebugConnectionJob$3.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/launch/DebugConnectionJob$4.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/launch/DebugConnectionJob.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/launch/WidgetLaunchDelegate.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/launch/WidgetTabSelector.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$1.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$2.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$3.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$4.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$5.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$6$1.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$6.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$7$1.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$7.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$8.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl$DebugEventListenerImpl.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/JavascriptThread.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/ResourceManager$ScriptIdentifier.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/ResourceManager.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/model/StackFrame.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/property/PropertyTester.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/web/EmulatorContext.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/web/WebAppInterface.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/web/WebappManager.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/internal/web/WorkspaceResourcesServlet.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/DebugPreferencePage.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/actions/JsBreakpointPropertiesRulerAction$1.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/actions/JsBreakpointPropertiesRulerAction.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/actions/JsBreakpointPropertiesRulerActionDelegate.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/launch/WidgetBasicTab$1.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/launch/WidgetBasicTab.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/launch/WidgetLaunchConfigurationTabGroup.class
org.symbian.tools.wrttools.debug.core/bin/org/symbian/tools/wrttools/debug/ui/launch/WidgetLaunchShortcut.class
org.symbian.tools.wrttools.debug.core/build.properties
org.symbian.tools.wrttools.debug.core/http-content/wrtdebugger/connectionTest.jsp
org.symbian.tools.wrttools.debug.core/http-content/wrtdebugger/debugger.jsp
org.symbian.tools.wrttools.debug.core/icons/deploy_settings.gif
org.symbian.tools.wrttools.debug.core/icons/deploy_widget.gif
org.symbian.tools.wrttools.debug.core/icons/error_over.gif
org.symbian.tools.wrttools.debug.core/icons/error_ovr.gif
org.symbian.tools.wrttools.debug.core/icons/exclude_archive.gif
org.symbian.tools.wrttools.debug.core/icons/icon.gif
org.symbian.tools.wrttools.debug.core/icons/icon32.gif
org.symbian.tools.wrttools.debug.core/icons/include_archive.gif
org.symbian.tools.wrttools.debug.core/icons/main16.gif
org.symbian.tools.wrttools.debug.core/icons/package_widget.gif
org.symbian.tools.wrttools.debug.core/icons/upgrade-project-over.gif
org.symbian.tools.wrttools.debug.core/icons/upgrade_project.gif
org.symbian.tools.wrttools.debug.core/icons/upgrade_red.gif
org.symbian.tools.wrttools.debug.core/icons/validate_widget.gif
org.symbian.tools.wrttools.debug.core/launch/WRT Debugger.launch
org.symbian.tools.wrttools.debug.core/plugin.xml
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/Activator.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/BreakpointAdapterFactory.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/ChromeDebugUtils.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/IConstants.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/Images.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/PreferenceInitializer.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/WorkspaceLineBreakpointAdapter.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/launch/DebugConnectionJob.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/launch/WidgetLaunchDelegate.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/launch/WidgetTabSelector.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/model/DebugTargetImpl.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/model/JavascriptThread.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/model/ResourceManager.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/model/StackFrame.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/property/PropertyTester.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/web/EmulatorContext.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/web/WebAppInterface.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/web/WebappManager.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/internal/web/WorkspaceResourcesServlet.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/ui/DebugPreferencePage.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/ui/actions/JsBreakpointPropertiesRulerAction.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/ui/actions/JsBreakpointPropertiesRulerActionDelegate.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/ui/launch/WidgetBasicTab.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/ui/launch/WidgetLaunchConfigurationTabGroup.java
org.symbian.tools.wrttools.debug.core/src/org/symbian/tools/wrttools/debug/ui/launch/WidgetLaunchShortcut.java
--- /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);
+	}
+}