bug#210212 (initial commit)
diff --git a/plugins/org.eclipse.actf.ai.fennec/.classpath b/plugins/org.eclipse.actf.ai.fennec/.classpath
new file mode 100644
index 0000000..751c8f2
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/plugins/org.eclipse.actf.ai.fennec/.cvsignore b/plugins/org.eclipse.actf.ai.fennec/.cvsignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/plugins/org.eclipse.actf.ai.fennec/.project b/plugins/org.eclipse.actf.ai.fennec/.project
new file mode 100644
index 0000000..6a55645
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.actf.ai.fennec</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>
diff --git a/plugins/org.eclipse.actf.ai.fennec/META-INF/MANIFEST.MF b/plugins/org.eclipse.actf.ai.fennec/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..738557c
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/META-INF/MANIFEST.MF
@@ -0,0 +1,18 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Eclipse ACTF Fennec Plug-in (Incubation)
+Bundle-SymbolicName: org.eclipse.actf.ai.fennec
+Bundle-Version: 0.0.1
+Bundle-Activator: org.eclipse.actf.ai.fennec.NVM3Plugin
+Bundle-Vendor: Eclipse.org
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.actf.util.vocab,
+ org.eclipse.actf.common,
+ org.eclipse.actf.model.dom.dombycom,
+ org.eclipse.actf.ai.query,
+ org.eclipse.actf.ai.xmlstore,
+ org.eclipse.swt
+Eclipse-LazyStart: true
+Export-Package: org.eclipse.actf.ai.fennec,
+ org.eclipse.actf.ai.fennec.treemanager
diff --git a/plugins/org.eclipse.actf.ai.fennec/about.html b/plugins/org.eclipse.actf.ai.fennec/about.html
new file mode 100644
index 0000000..481dbcf
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/about.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>June 5, 2006</p>	
+<h3>License</h3>
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  
+Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/org/documents/epl-v10.php">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor&rsquo;s license 
+that was provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/org.eclipse.actf.ai.fennec/build.properties b/plugins/org.eclipse.actf.ai.fennec/build.properties
new file mode 100644
index 0000000..e1942be
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/build.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2007 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+			   about.html,\
+               .
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/INVM3Entry.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/INVM3Entry.java
new file mode 100644
index 0000000..cc7e42a
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/INVM3Entry.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec;
+
+import java.io.File;
+
+
+
+public interface INVM3Entry {
+    String getDocumentation();
+    boolean isUserEntry();
+    boolean export(File dest);
+}
+
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/INVM3Mediator.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/INVM3Mediator.java
new file mode 100644
index 0000000..c491712
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/INVM3Mediator.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec;
+
+import org.eclipse.actf.ai.fennec.treemanager.ITreeManager;
+
+
+
+public interface INVM3Mediator {
+    ITreeManager newTreeManager(INVM3Entry entry);
+    INVM3Entry getDefaultNVM3Entry();
+    INVM3Entry[] getNVM3Entries();
+    void release();
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/INVM3Service.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/INVM3Service.java
new file mode 100644
index 0000000..9620af5
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/INVM3Service.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec;
+
+import org.eclipse.actf.ai.fennec.treemanager.IAccessKeyList;
+import org.eclipse.actf.ai.fennec.treemanager.ISoundControl;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.fennec.treemanager.IVideoControl;
+import org.eclipse.actf.model.dom.dombycom.IFlashNode;
+import org.eclipse.actf.util.vocab.IProposition;
+import org.w3c.dom.Node;
+
+
+public interface INVM3Service {
+    int UNINIT = 0;
+    int NORMAL = 1;
+    int ERROR = 1 << 16;
+
+    int getStatus();
+    ITreeItem getLastTreeItem();
+    ISoundControl getSoundControl();
+    IVideoControl getVideoControl();
+    IAccessKeyList getAccessKeyList();
+    IFlashNode[] getFlashTopNodes();
+
+    int initialize() throws NVM3Exception;
+    int moveUpdate(ITreeItem target) throws NVM3Exception;
+    int moveUpdate(ITreeItem target, boolean update) throws NVM3Exception;
+    int clickUpdate(ITreeItem target) throws NVM3Exception;
+
+    int searchForward(IProposition prop) throws NVM3Exception;
+    int searchBackward(IProposition prop) throws NVM3Exception;
+
+    int analyze() throws NVM3Exception;
+
+    int skipToAnchor(String target) throws NVM3Exception;
+
+    // !!FN!!
+    ITreeItem expandWholeTree() throws NVM3Exception;
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/NVM3Exception.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/NVM3Exception.java
new file mode 100644
index 0000000..668d79e
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/NVM3Exception.java
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec;
+
+public class NVM3Exception extends Exception {
+    private static final long serialVersionUID = -4473389024467270039L;
+
+    public NVM3Exception(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/NVM3InterruptedException.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/NVM3InterruptedException.java
new file mode 100644
index 0000000..02c503a
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/NVM3InterruptedException.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec;
+
+public class NVM3InterruptedException extends NVM3Exception {
+    private static final long serialVersionUID = 503781748413609768L;
+
+    public NVM3InterruptedException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public NVM3InterruptedException(String message) {
+        super(message, null);
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/NVM3Plugin.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/NVM3Plugin.java
new file mode 100644
index 0000000..64ad7b8
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/NVM3Plugin.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec;
+
+import org.eclipse.actf.ai.fennec.mediator.NVM3MediatorImpl;
+import org.eclipse.actf.model.IWebBrowserACTF;
+import org.eclipse.core.runtime.Plugin;
+import org.osgi.framework.BundleContext;
+
+
+
+
+public class NVM3Plugin extends Plugin {
+
+    //The shared instance.
+    private static NVM3Plugin plugin;
+        
+    /**
+     * The constructor.
+     */
+    public NVM3Plugin() {
+        plugin = this;
+    }
+
+    /**
+     * This method is called upon plug-in activation
+     */
+    @Override
+    public void start(BundleContext context) throws Exception {
+        super.start(context);
+    }
+
+    /**
+     * This method is called when the plug-in is stopped
+     */
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        super.stop(context);
+        plugin = null;
+    }
+
+    /**
+     * Returns the shared instance.
+     */
+    public static NVM3Plugin getDefault() {
+        return plugin;
+    }
+
+    public INVM3Mediator newNVM3Mediator(IWebBrowserACTF webBrowser) {
+        return new NVM3MediatorImpl(webBrowser);
+    }
+
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/NVM3ServiceFactory.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/NVM3ServiceFactory.java
new file mode 100644
index 0000000..9399fec
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/NVM3ServiceFactory.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec;
+
+import org.eclipse.actf.ai.fennec.impl.NVM3ServiceImpl;
+import org.eclipse.actf.model.dom.dombycom.IDocumentEx;
+
+
+public class NVM3ServiceFactory {
+    public static INVM3Service newNVM3Service(INVM3Entry entry, IDocumentEx doc) throws NVM3Exception {
+        return new NVM3ServiceImpl(entry, doc);
+    }
+
+    public static INVM3Service newNVM3ServiceWithDefaultMetadata(IDocumentEx doc) {
+        return new NVM3ServiceImpl(doc);
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/autotranslator/AutoTranslator.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/autotranslator/AutoTranslator.java
new file mode 100644
index 0000000..05b77ad
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/autotranslator/AutoTranslator.java
@@ -0,0 +1,248 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.autotranslator;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.actf.ai.fennec.impl.NVM3Mode;
+import org.eclipse.actf.ai.fennec.impl.TreeItemMark;
+import org.eclipse.actf.ai.fennec.impl.TreeItemNVM3;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.model.dom.dombycom.IFlashNode;
+import org.eclipse.actf.model.dom.dombycom.IImageElement;
+import org.eclipse.actf.model.dom.dombycom.INodeEx;
+import org.eclipse.actf.util.vocab.Vocabulary;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+
+public class AutoTranslator {
+    private static final boolean AUTO_COLLAPSE = true;
+
+    private static final boolean AUTO_INDENT = true;
+
+    private static boolean canCollapse(TreeItemNVM3 item) {
+        if (true) {
+            return Vocabulary.isReducible().eval(item);
+        } else {
+            if (Vocabulary.hasContent().eval(item))
+                return false;
+            ITreeItem[] cc = item.getChildItems();
+            for (int i = 0; i < cc.length; i++) {
+                TreeItemNVM3 item2 = (TreeItemNVM3) cc[i];
+                if (Vocabulary.hasContent().eval(item2))
+                    return false;
+            }
+            return true;
+        }
+    }
+
+    private static TreeItemNVM3 simplify(TreeItemNVM3 item, TreeItemNVM3 pItem) {
+        if (AUTO_COLLAPSE) {
+            // pattern pItem - item(X) - child => pItem - child
+            if (!Vocabulary.hasContent().eval(item)) {
+                ITreeItem[] cc = item.getChildItems();
+                if (cc.length == 0) {
+                    return null;
+                }
+                if (cc.length == 1) {
+                    TreeItemNVM3 item2 = (TreeItemNVM3) cc[0];
+                    item2.forceParent(pItem);
+                    item2.addMetadata(item);
+                    return item2;
+                }
+            }
+            
+            // pattern item - child(X) - grandChild => item - grandChild
+            ITreeItem[] childItems = item.getChildItems();
+            if (childItems.length == 1) {
+                TreeItemNVM3 item2 = (TreeItemNVM3) childItems[0];
+                if (!Vocabulary.hasContent().eval(item2)){
+                    ITreeItem[] childItems2 = item2.getChildItems();
+                    item.setChildItems(childItems2);
+                    item.addMetadata(item2);
+                }
+            }
+        }
+
+        return item;
+    }
+
+    private static void adjustLabelIndent(TreeItemNVM3 item) {
+        // TODO
+        //        var itemCount = item._childItems.length;
+        //        if( itemCount>1 ) {
+        //            var heading;
+        //            var topIsLabel = true;
+        //            for( var i=0, child; child=item._childItems[i]; i++ ) {
+        //            if( topIsLabel ) {
+        //                topIsLabel = child._childItems.length ? (i>0) : (i==0);
+        //            }
+        //            if( child.isHeadingNode() ) {
+        //                // Collapse heading node tree
+        //                child._join();
+        //                heading = child._childItems.length==0 ? child : null;
+        //            }
+        //            else if( heading ) {
+        //                // Move child item into heading item children
+        //                heading._childItems.push(child);
+        //                item._childItems.splice(i--,1);
+        //                topIsLabel = false;
+        //                        heading._labelHead = true;
+        //            }
+        //            }
+        //            if( topIsLabel ) {
+        //            // 1st child is a label. Move item 2-n into item 1 children
+        //                    heading = item._childItems[0];
+        //            heading._childItems = item._childItems.splice(1,item._childItems.length-1);
+        //                    heading._labelHead = true;
+        //            }
+        //            if( (item._childItems.length != itemCount) && Options.AUTO_COLLAPSE ) {
+        //            // Re-adjust tree collaption because tree structure has changed
+        //            for( var i=0, child; child=item._childItems[i]; i++ ) {
+        //                child._simplify();
+        //            }
+        //            item._simplify();
+        //            }
+        //        }
+
+    }
+
+    private static List<ITreeItem> buildTreeItemContinued(NVM3Mode mode, TreeItemNVM3 item, INodeEx nex, int depth) {
+        List<ITreeItem> childItemList = new ArrayList<ITreeItem>();
+        ITreeItem lastItem = null;
+        
+        if (nex instanceof IFlashNode) {
+            IFlashNode fn = (IFlashNode) nex;
+            nex = fn.getMSAA();
+            lastItem = new TreeItemMark(item, TreeItemMark.MarkType.FLASH_END);
+            
+            if ((nex != null && Vocabulary.getNormalFlashMode() == Vocabulary.FlashMode.NO_FLASH)
+                    || (nex == null && Vocabulary.getWindowlessFlashMode() == Vocabulary.FlashMode.NO_FLASH)) {
+                return childItemList;
+            }
+            
+            if ((nex != null && Vocabulary.getNormalFlashMode() == Vocabulary.FlashMode.FLASH_DOM)
+                    || (nex == null && Vocabulary.getWindowlessFlashMode() == Vocabulary.FlashMode.FLASH_DOM)) {
+                IFlashNode[] translated = fn.translate();
+                for (int i = 0; i < translated.length; i++) {
+                    TreeItemNVM3 newItem = mode.generateItem(item, translated[i]);
+                    if (newItem == null) continue;
+                    childItemList.add(newItem);
+                }
+                if (Vocabulary.isFlashTopNode().eval(fn)) {
+                    childItemList.add(lastItem);
+                }
+                
+                item.setChildItems(childItemList);
+                return childItemList;
+            }
+        }
+        
+        if (nex instanceof IImageElement) {
+            IImageElement image = (IImageElement) nex;
+            if (image.hasUsemap()) {
+                Element map = image.getMap();
+                if (map == null) return childItemList;
+                NodeList nl = map.getChildNodes();
+                for (int i = 0; i < nl.getLength(); i++) {
+                    Node n = nl.item(i);
+                    if (n instanceof Element) {
+                        TreeItemNVM3 newItem = mode.generateItem(item, n);
+                        childItemList.add(newItem);
+                    }
+                }
+                item.setChildItems(childItemList);
+                return childItemList;
+            }
+        }
+
+        if (depth <= 0) {
+            item.setChildItems(childItemList);
+            return childItemList;
+        }
+
+        TreeItemNVM3 childItem = null;
+        NodeList nl = nex.getChildNodes();
+        int len = nl.getLength();
+        boolean hasContent = Vocabulary.hasContent().eval(item);
+        for (int i = 0; i < len; i++) {
+            Node cn = nl.item(i);
+
+            if (hasContent) {
+                childItem = buildTreeItem(mode, item, cn, depth - 1);
+            } else {
+                childItem = buildTreeItem(mode, item, cn, depth);
+            }
+            if (childItem == null)
+                continue;
+
+            if (true && AUTO_COLLAPSE && canCollapse(childItem)) {
+                ITreeItem[] cc = childItem.getChildItems();
+                if ((cc != null) && (cc.length > 0)) {
+                    for (int j = 0; j < cc.length; j++) {
+                        if (!(cc[j] instanceof TreeItemNVM3))
+                            continue;
+                        TreeItemNVM3 temp = (TreeItemNVM3) cc[j];
+                        temp.addMetadata(childItem);
+                    }
+                    childItemList.addAll(Arrays.asList(cc));
+                }
+            } else {
+                childItemList.add(childItem);
+            }
+        }
+        
+        if (lastItem != null)
+            childItemList.add(lastItem);
+
+        item.setChildItems(childItemList);
+        return childItemList;
+    }
+
+    private static TreeItemNVM3 buildTreeItem(NVM3Mode mode, TreeItemNVM3 pItem, Node n, int depth) {
+        if (!(n instanceof INodeEx)) {
+            return null;
+        }
+
+        INodeEx  nex = (INodeEx) n;
+
+        if (!Vocabulary.isValidNode().eval(nex)) {
+            return null;
+        }
+
+        TreeItemNVM3 item = mode.generateItem(pItem, n);
+        if (item == null) return null;
+        buildTreeItemContinued(mode, item, nex, depth);
+
+        if (AUTO_INDENT) {
+            adjustLabelIndent(item);
+        }
+
+        return simplify(item, pItem);
+    }
+
+    public static TreeItemNVM3 translate(NVM3Mode mode, TreeItemNVM3 pItem, Node n) {
+
+        return buildTreeItem(mode, pItem, n, 1);
+    }
+
+    public static TreeItemNVM3 translateContinued(NVM3Mode mode, TreeItemNVM3 item, Node n) {
+        if (!(n instanceof INodeEx))
+            return item;
+        buildTreeItemContinued(mode, item, (INodeEx) n, 2);
+        // item = simplify(item, (TreeItemNVM3) item.getParent());
+        return item;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3BundleMetadata.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3BundleMetadata.java
new file mode 100644
index 0000000..015beb0
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3BundleMetadata.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.actf.ai.fennec.NVM3Exception;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.query.IQuery;
+import org.w3c.dom.Node;
+
+
+abstract class NVM3BundleMetadata extends NVM3Metadata {
+    private static final NVM3Metadata[] emptyChildMetadata = new NVM3Metadata[0];
+    protected final NVM3Metadata[] childMetadata;
+
+    protected NVM3BundleMetadata(NVM3ServiceImpl nvm3Service,
+                                 IQuery q, NVM3Mode mode, NVM3Metadata[] mds) {
+        super(nvm3Service, q, mode);
+        if (mds == null) {
+            mds = emptyChildMetadata;
+        }
+        this.childMetadata = mds;
+    }
+
+    protected NVM3BundleMetadata(NVM3ServiceImpl nvm3Service,
+                                NVM3Mode mode,
+                                Node node,
+                                NVM3Metadata[] mds) {
+        super(nvm3Service, mode, node);
+        if (mds == null) {
+            mds = emptyChildMetadata;
+        }
+        this.childMetadata = mds;
+    }
+
+    @Override
+    List expand(TreeItemNVM3 pItem, int trigger) throws NVM3Exception {
+        ArrayList ret = new ArrayList();
+        for (int i = 0; i < childMetadata.length; i++) {
+            NVM3Metadata cmd = childMetadata[i];
+            List ret2 = cmd.buildItems(pItem, null, trigger);
+            if (ret2 != null) ret.addAll(ret2);
+        }
+        return ret;
+    }
+
+
+    NVM3Metadata[] getChildMetadata() {
+        return childMetadata;
+    }
+
+    // CoR pattern.
+    @Override
+    public String getAltText(ITreeItem item) {
+        for (int i = 0; i < childMetadata.length; i++) {
+            if (childMetadata[i].hasTargets()) continue;
+            if (!(childMetadata[i] instanceof NVM3SimpleMetadata)) continue;
+            String ret = childMetadata[i].getAltText(item);
+            if (ret == null) return null;
+            if (ret.length() > 0) return ret;
+        }
+        return "";
+    }
+
+    @Override
+    public String getDescription(ITreeItem item) {
+        for (int i = 0; i < childMetadata.length; i++) {
+            if (childMetadata[i].hasTargets()) continue;
+            if (!(childMetadata[i] instanceof NVM3SimpleMetadata)) continue;
+            String ret = childMetadata[i].getDescription(item);
+            if (ret == null) return null;
+            if (ret.length() > 0) return ret;
+        }
+        return "";
+    }
+
+    @Override
+    public short getHeadingLevel(ITreeItem item) {
+        for (int i = 0; i < childMetadata.length; i++) {
+            if (childMetadata[i].hasTargets()) continue;
+            if (!(childMetadata[i] instanceof NVM3SimpleMetadata)) continue;
+            short ret = childMetadata[i].getHeadingLevel(item);
+            if (ret == -1) return -1;
+            if (ret > 0) return ret;
+        }
+        return 0;
+    }
+}
+
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3DOMReader.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3DOMReader.java
new file mode 100644
index 0000000..5cd52b4
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3DOMReader.java
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+
+import org.eclipse.actf.ai.fennec.INVM3Entry;
+import org.eclipse.actf.ai.query.IQuery;
+import org.eclipse.actf.ai.query.QueryService;
+import org.eclipse.actf.ai.xmlstore.IXMLInfo;
+import org.eclipse.actf.ai.xmlstore.XMLStoreException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+
+public class NVM3DOMReader {
+    public static final String NVM3_NAMESPACE_URI = "http://www.ibm.com/xmlns/prod/aiBrowser/fennec";
+    public static final String NVM3_DOCUMENT_ELEMENT_NAME = "fennec";
+
+    private NVM3ServiceImpl nvm3Service;
+
+    private void error(String message) {
+        System.err.println(message);
+    }
+
+    private IQuery parseQuery(Element e, IQuery parentQuery) {
+        return QueryService.parse(e, parentQuery);
+    }
+
+    private NVM3Metadata parseNode(NVM3Mode mode, Element e, IQuery parentQuery) {
+        NodeList nl = e.getChildNodes();
+        IQuery q = parseQuery(e, parentQuery);
+        if (q != null) parentQuery = q;
+        NVM3Metadata[] mds = parseInternal(mode, nl, parentQuery);
+        return new NVM3GroupMetadata(nvm3Service, q, mode, mds);
+    }
+
+    private NVM3Metadata parseHeader(NVM3Mode mode, Element e, short level, IQuery parentQuery) {
+        // IQuery q = parseQuery(e, parentQuery);
+        return NVM3SimpleMetadata.newHeader(nvm3Service, null, mode, level);
+    }
+
+    private List parseTextFormat(Element e, IQuery parentQuery) {
+        NodeList nl = e.getChildNodes();
+        int len = nl.getLength();
+        if (len == 0) return null;
+        ArrayList al = new ArrayList();
+        for (int i = 0; i < len; i++) {
+            Node n = nl.item(i);
+            switch (n.getNodeType()) {
+            case Node.TEXT_NODE:
+                al.add(n.getNodeValue());
+                break;
+            case Node.ELEMENT_NODE:
+                Element ce = (Element) n;
+                if ("ref".equals(ce.getLocalName())) {
+                    IQuery q = parseQuery((Element) n, parentQuery);
+                    al.add(q);
+                } else {
+                    error("Invalid element in text format:" + n.getNodeName());
+                }
+                break;
+            }
+        }
+        return al;
+    }
+
+    private NVM3Metadata parseAltText(NVM3Mode mode, Element e, IQuery parentQuery) {
+        IQuery q = parseQuery(e, parentQuery);
+        if (q != null) parentQuery = q;
+        List l = parseTextFormat(e, parentQuery);
+        return NVM3SimpleMetadata.newAltText(nvm3Service, q, mode, l);
+    }
+
+    private NVM3Metadata parseDescription(NVM3Mode mode, Element e, IQuery parentQuery) {
+        IQuery q = parseQuery(e, parentQuery);
+        if (q != null) parentQuery = q;
+        List l = parseTextFormat(e, parentQuery);
+        return NVM3SimpleMetadata.newDescription(nvm3Service, mode, q, l);
+    }
+
+    private boolean parseBoolean(String str) {
+        String str2 = str.trim();
+        if (str2.equals("0")) {
+            return false;
+        } else if (str2.equals("false")) {
+            return false;
+        } else if (str2.equals("1")) {
+            return true;
+        } else if (str2.equals("true")) {
+            return true;
+        }
+        error("Invalide boolean value:" + str2);
+        return false;
+    }
+
+    private int parseTrigger(String str) {
+        String str2 = str.trim();
+        if ("always".equals(str2)) {
+            return NVM3Mode.TRIGGER_ALWAYS;
+        } else if ("move".equals(str2)) {
+            return NVM3Mode.TRIGGER_MOVE;
+        } else if ("click".equals(str2)) {
+            return NVM3Mode.TRIGGER_CLICK;
+        } else if (str.length() == 0) {
+            return NVM3Mode.TRIGGER_ALWAYS;
+        } else {
+            error("Invalid trigger attribute:" + str2);
+        }
+        return NVM3Mode.TRIGGER_MOVE;
+    }
+
+    private NVM3Metadata parseInternalForElement(NVM3Mode mode, Element e, IQuery parentQuery) {
+        String name = e.getLocalName();
+        if (name.equals("node")) {
+            return parseNode(mode, e, parentQuery);
+        } else if (name.equals("table")) {
+            // TODO
+        } else if (name.equals("ul")) {
+            // TODO
+        } else if (name.equals("ol")) {
+            // TODO
+        } else if (name.equals("h-")) {
+            return parseHeader(mode, e, (short) -1, parentQuery);
+        } else if (name.equals("h1")) {
+            return parseHeader(mode, e, (short) 1, parentQuery);
+        } else if (name.equals("h2")) {
+            return parseHeader(mode, e, (short) 2, parentQuery);
+        } else if (name.equals("h3")) {
+            return parseHeader(mode, e, (short) 3, parentQuery);
+        } else if (name.equals("h4")) {
+            return parseHeader(mode, e, (short) 4, parentQuery);
+        } else if (name.equals("h5")) {
+            return parseHeader(mode, e, (short) 5, parentQuery);
+        } else if (name.equals("h6")) {
+            return parseHeader(mode, e, (short) 6, parentQuery);
+        } else if (name.equals("altText")) {
+            return parseAltText(mode, e, parentQuery);
+        } else if (name.equals("description")) {
+            return parseDescription(mode, e, parentQuery);
+        } else if (name.equals("metadata")) {
+            // TODO
+        } else if (name.equals("keyDescription")) {
+            // TODO
+        } else if (name.equals("attach")) {
+            NodeList nl = e.getChildNodes();
+            IQuery q = parseQuery(e, parentQuery);
+            if (q == null) {
+                // TODO;
+            }
+
+            String triggerStr = e.getAttributeNS(null, "trigger");
+            int trigger = parseTrigger(triggerStr);
+
+            String autoStr = e.getAttributeNS(null, "auto");
+            boolean auto;
+            if (autoStr.length() == 0) {
+                auto = true;
+            } else {
+                auto = parseBoolean(autoStr);
+            }
+            String changelessStr = e.getAttributeNS(null, "changeless");
+            boolean changeless;
+            if (changelessStr.length() == 0) {
+                changeless = false;
+            } else {
+                changeless = parseBoolean(changelessStr);
+            }
+            String waitContentsStr = e.getAttributeNS(null, "waitContents");
+            boolean waitContents;
+            if (waitContentsStr.length() == 0) {
+                waitContents = false;
+            } else {
+                waitContents = parseBoolean(waitContentsStr);
+            }
+
+            NVM3Mode nextMode = new NVM3Mode(NVM3Mode.TYPE_ATTACH, trigger,
+                                             auto, changeless, waitContents);
+
+            NVM3Metadata[] mds = parseInternal(nextMode, nl, q);
+            return NVM3RecombinantMetadata.newAttach(nvm3Service, q, mode, nextMode, mds);
+        } else if (name.equals("unwrap")) {
+            NodeList nl = e.getChildNodes();
+            IQuery q = parseQuery(e, parentQuery);
+            if (q == null) {
+                // TODO;
+            }
+
+            String triggerStr = e.getAttributeNS(null, "trigger");
+            int trigger = parseTrigger(triggerStr);
+
+            NVM3Mode nextMode = new NVM3Mode(NVM3Mode.TYPE_UNWRAP, trigger, false, false, false);
+
+            NVM3Metadata[] mds = parseInternal(nextMode, nl, q);
+            return NVM3RecombinantMetadata.newUnwrap(nvm3Service, q, mode, nextMode, mds);
+        }
+        return null;
+    }
+
+    private NVM3Metadata[] parseInternal(NVM3Mode mode, NodeList nl, IQuery parentQuery) {
+        ArrayList al = new ArrayList();
+        int len = nl.getLength();
+        for (int i = 0; i < len; i++) {
+            Node n = nl.item(i);
+            if (n.getNodeType() == Node.ELEMENT_NODE) {
+                String ns = n.getNamespaceURI();
+                if (ns.equals(NVM3_NAMESPACE_URI)) {
+                    Element e = (Element) n;
+                    NVM3Metadata md = parseInternalForElement(mode, e, parentQuery);
+                    if (md != null) al.add(md);
+                }
+            }
+        }
+        return (NVM3Metadata[]) al.toArray(new NVM3Metadata[0]);
+    }
+
+    private NVM3Metadata parseTop(Element e) {
+        String namespaceURI = e.getNamespaceURI();
+        if (!namespaceURI.equals(NVM3_NAMESPACE_URI)) {
+            error("The namespace URI of the document element must be " + NVM3_NAMESPACE_URI);
+            return null;
+        }
+        String localName = e.getLocalName();
+        if (!(NVM3_DOCUMENT_ELEMENT_NAME.equals(localName))) {
+            error("The document element is not nvm3");
+        }
+        NVM3Mode rootMode = new NVM3Mode(NVM3Mode.TYPE_SIMPLE);
+
+        IQuery q = parseQuery(e, null);
+        NVM3Metadata[] mds = parseInternal(rootMode, e.getChildNodes(), q);
+        if (mds.length == 0) {
+            return null;
+        }
+        
+        return new NVM3GroupMetadata(nvm3Service, q, rootMode, mds);
+    }
+
+    public NVM3Metadata parse(INVM3Entry entry) throws XMLStoreException {
+        NVM3EntryImpl ei = (NVM3EntryImpl) entry;
+        IXMLInfo info = ei.getIXMLInfo();
+        Node n = info.getRootNode();
+        Element e;
+        if (n instanceof Document) {
+            e = ((Document) n).getDocumentElement();
+        } else if (n instanceof Element) {
+            e = (Element) n;
+        } else {
+            throw new XMLStoreException("Failed to load NVM3.", null);
+        }
+        return parseTop(e);
+    }
+
+    NVM3DOMReader(NVM3ServiceImpl nvm3Service) {
+        this.nvm3Service = nvm3Service;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3EntryImpl.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3EntryImpl.java
new file mode 100644
index 0000000..4bfdb01
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3EntryImpl.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.impl;
+
+import java.io.File;
+
+import org.eclipse.actf.ai.fennec.INVM3Entry;
+import org.eclipse.actf.ai.xmlstore.IXMLInfo;
+import org.eclipse.actf.ai.xmlstore.XMLStorePlugin;
+
+
+
+
+public class NVM3EntryImpl implements INVM3Entry {
+    private IXMLInfo info;
+
+    public String getDocumentation() {
+        return info.getDocumentation();
+    }
+
+    public boolean isUserEntry() {
+        return info.isUserEntry();
+    }
+        
+    public IXMLInfo getIXMLInfo() {
+        return info;
+    }
+
+    public NVM3EntryImpl(IXMLInfo info) {
+        this.info = info;
+    }
+
+    public boolean export(File dest) {
+        return XMLStorePlugin.getDefault().getXMLStoreService().exportMetadata(info, dest);
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3GeneratedMetadata.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3GeneratedMetadata.java
new file mode 100644
index 0000000..f885cca
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3GeneratedMetadata.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.impl;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.actf.ai.fennec.NVM3Exception;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.model.dom.dombycom.INodeEx;
+import org.w3c.dom.Node;
+
+
+class NVM3GeneratedMetadata extends NVM3BundleMetadata {
+    private final Node node;
+    
+    @Override
+    public String getAltText(ITreeItem item) {
+        String r = super.getAltText(item);
+        if (r == null) return null;
+        if (r.length() > 0)
+            return r;
+        if (node instanceof INodeEx) {
+            return ((INodeEx) node).extractString();
+        }
+        return "";
+    }
+
+    @Override
+    public String getDescription(ITreeItem item) {
+        return super.getDescription(item);
+    }
+
+    public short getHeadingLevelByMetadata(ITreeItem item) {
+        return super.getHeadingLevel(item);
+    }
+
+    @Override
+    public short getHeadingLevel(ITreeItem item) {
+        short r = super.getHeadingLevel(item);
+        if (r > 0)
+            return r;
+        if (r == -1)
+            return -1;
+        if (node instanceof INodeEx) {
+            return ((INodeEx) node).getHeadingLevel();
+        }
+        return 0;
+    }
+
+    @Override
+    List expand(TreeItemNVM3 pItem, int trigger) throws NVM3Exception {
+        ITreeItem[] childItems = pItem.getChildItems();
+        if (!mode.changed(pItem, trigger) && pItem.hasAlreadyChildRefreshed()) {
+            return Arrays.asList(childItems);
+        } else {
+            TreeItemNVM3 bItem = mode.buildItemContinued(node, pItem);
+            if ((bItem != null) && bItem.hasChild()) {
+                return Arrays.asList(bItem.getChildItems());
+            }
+        }
+        return null;
+    }
+
+    public static NVM3GeneratedMetadata generate(NVM3RecombinantMetadata metadata, NVM3Mode mode, Node node,
+            NVM3Metadata[] childMds) {
+        return new NVM3GeneratedMetadata(metadata.nvm3Service, mode, node, childMds);
+    }
+
+    private NVM3GeneratedMetadata(NVM3ServiceImpl nvm3Service, NVM3Mode mode, Node node, NVM3Metadata[] childMds) {
+        super(nvm3Service, mode, node, childMds);
+        this.node = node;
+    }
+
+    public static NVM3Metadata generate(NVM3GeneratedMetadata meta, NVM3GeneratedMetadata meta2) {
+        NVM3Metadata[] childMds = new NVM3Metadata[meta.childMetadata.length+meta2.childMetadata.length];
+        for(int i=0; i<meta.childMetadata.length; i++)
+            childMds[i] = meta.childMetadata[i];
+        for(int i=0; i<meta2.childMetadata.length; i++)
+            childMds[meta.childMetadata.length+i] = meta2.childMetadata[i];
+        return new NVM3GeneratedMetadata(meta.nvm3Service, meta.mode, meta.node, childMds);
+    }
+
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3GroupMetadata.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3GroupMetadata.java
new file mode 100644
index 0000000..174f1e5
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3GroupMetadata.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.impl;
+
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.query.IQuery;
+import org.eclipse.actf.model.dom.dombycom.INodeEx;
+
+
+class NVM3GroupMetadata extends NVM3BundleMetadata {
+    
+    public String getAltText(ITreeItem item) {
+        String r = super.getAltText(item);
+        if (r == null) return null;
+        if (r.length() > 0) return r;
+        // If not specified, extract some string from the node.
+        return NodeUtil.extractString(item);
+    }
+
+    public short getHeadingLevel(ITreeItem item) {
+        short r = super.getHeadingLevel(item);
+        if (r > 0) return r;
+        if (r == -1) return -1;
+        Object baseNode = item.getBaseNode();
+        if (baseNode instanceof INodeEx) {
+            return ((INodeEx) baseNode).getHeadingLevel();
+        }
+        return 0;
+    }
+
+    NVM3GroupMetadata(NVM3ServiceImpl nvm3Service,
+                      IQuery q, NVM3Mode mode,
+                      NVM3Metadata[] childMds) {
+        super(nvm3Service, q, mode, childMds);
+    }
+
+}
+
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3Metadata.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3Metadata.java
new file mode 100644
index 0000000..367b524
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3Metadata.java
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.actf.ai.fennec.NVM3Exception;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.query.IQuery;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+
+abstract class NVM3Metadata {
+    protected final NVM3ServiceImpl nvm3Service;
+    private final IQuery query;
+
+    private final boolean hasTarget;
+    public boolean hasTargets() {
+        return hasTarget;
+    }
+
+    private static class EmptyNodeList implements NodeList {
+        public Node item(int index) {
+            return null;
+        }
+
+        public int getLength() {
+            return 0;
+        }
+    }
+    private static final NodeList emptyNodeList = new EmptyNodeList();
+
+    NodeList query(IQuery q, Node baseNode) {
+        if ((q == null) || (!q.hasTarget())) return emptyNodeList;
+        return q.query(baseNode);
+    }
+
+    NodeList query(IQuery q, ITreeItem baseItem) {
+        if ((q == null) || (!q.hasTarget())) return emptyNodeList;
+        Node baseNode;
+        for (baseNode = null;
+            (baseItem != null) && (baseNode == null);
+             baseItem = baseItem.getParent()) {
+            baseNode = (Node) baseItem.getBaseNode();
+        }
+        if (baseNode == null) {
+            baseNode = nvm3Service.getDocumentElement();
+        }
+        if (baseNode == null) return emptyNodeList;
+        return q.query(baseNode);
+    }
+
+    NodeList query(Node baseNode) {
+        return query(this.query, baseNode);
+    }
+
+    NodeList query(ITreeItem baseItem) {
+        return query(this.query, baseItem);
+    }
+
+    protected final NVM3Mode mode;
+
+    public abstract String getAltText(ITreeItem item);
+    public abstract String getDescription(ITreeItem item);
+    public abstract short getHeadingLevel(ITreeItem item);
+
+    List buildItems(TreeItemNVM3 baseItem, Node baseNode, int trigger) throws NVM3Exception {
+        if (hasTargets()) {
+            NodeList nl;
+            if (baseNode != null) {
+                nl = query(baseNode);
+            } else {
+                nl = query(baseItem);
+            }
+            int len = nl.getLength(); 
+            ArrayList result = new ArrayList(len);
+            for (int i = 0; i < len; i++) {
+                Node n = nl.item(i);
+                TreeItemNVM3 newItem = TreeItemNVM3.newTreeItem(this, baseItem, n);
+                if (newItem != null) {
+                    result.add(newItem);
+                }
+            }
+            return result;
+        } else if (this instanceof NVM3BundleMetadata) {
+            ArrayList result = new ArrayList(1);
+            TreeItemNVM3 newItem = TreeItemNVM3.newTreeItem(this, baseItem, null);
+            if (newItem != null) {
+                result.add(newItem);
+            }
+            return result;
+        }
+        return null;
+    }
+
+    TreeItemNVM3 buildRootItem() throws NVM3Exception {
+        List items = buildItems(null, null, NVM3Mode.TRIGGER_MOVE);
+        if ((items == null) || (items.size() == 0)) {
+            return null;
+        }
+        if (items.size() == 1) {
+            TreeItemNVM3 root = (TreeItemNVM3) items.get(0);
+            return root.expand(NVM3Mode.TRIGGER_MOVE);
+        } else {
+            TreeItemNVM3 root = TreeItemNVM3.newTreeItem(this, null, nvm3Service.getDocumentElement());
+            if (root == null) return null;
+            root.setChildItems(items);
+            return root;
+        }
+    }
+
+    abstract List expand(TreeItemNVM3 pItem, int trigger) throws NVM3Exception;
+
+    protected NVM3Metadata(NVM3ServiceImpl nvm3Service,
+                           IQuery q, NVM3Mode mode) {
+        this.nvm3Service = nvm3Service;
+        this.query = q;
+        if (q != null) {
+            this.hasTarget = q.hasTarget();
+        } else {
+            this.hasTarget = false;
+        }
+        this.mode = mode;
+        this.mode.addMetadata(this);
+    }
+
+    protected NVM3Metadata(NVM3ServiceImpl nvm3Service,
+                           NVM3Mode mode,
+                           Node node) {
+        this.nvm3Service = nvm3Service;
+        this.query = null;
+        this.hasTarget = false;
+        this.mode = mode;
+        this.mode.addMetadata(this);
+    }
+
+}
+
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3Mode.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3Mode.java
new file mode 100644
index 0000000..8a155d2
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3Mode.java
@@ -0,0 +1,292 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.eclipse.actf.ai.fennec.NVM3Exception;
+import org.eclipse.actf.ai.fennec.NVM3InterruptedException;
+import org.eclipse.actf.ai.fennec.autotranslator.AutoTranslator;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+
+// TODO I'd like to make it package-local.
+public class NVM3Mode {
+    static public final int TYPE_SIMPLE = 0;
+
+    static public final int TYPE_ATTACH = 1;
+
+    static public final int TYPE_UNWRAP = 2;
+
+    static public final int TYPE_BUILT = 2;
+
+    static public final int TRIGGER_MOVE = 1 << 0;
+
+    static public final int TRIGGER_CLICK = 1 << 1;
+
+    static public final int TRIGGER_KEEP = 1 << 2;
+
+    static public final int TRIGGER_WITHOUTCHANGE = 1 << 3;
+
+    static public final int TRIGGER_UNWRAP = 1 << 4;
+
+    static public final int TRIGGER_ALWAYS = (TRIGGER_MOVE
+                                              | TRIGGER_CLICK
+                                              | TRIGGER_KEEP
+                                              | TRIGGER_UNWRAP);
+
+    private final int type;
+
+    private final int trigger;
+
+    private final boolean automatic;
+
+    private final boolean changeless;
+
+    private final boolean waitContents;
+
+    private NVM3RecombinantMetadata baseMetadata;
+
+    private List<NVM3Metadata> belongingMetadata;
+
+    private HashMap<Node, ArrayList<NVM3Metadata>> metadataMap;
+
+    void addMetadata(NVM3Metadata md) {
+        if (false) {
+            // A mode manages its belonging metadata in order to manage substate, but
+            // currently it is not used...  So now this part is disabled.
+            if (belongingMetadata == null) {
+                belongingMetadata = new ArrayList<NVM3Metadata>();
+            }
+            belongingMetadata.add(md);
+        }
+    }
+
+    void setBaseMetadata(NVM3RecombinantMetadata md) {
+        this.baseMetadata = md;
+    }
+
+    boolean changed(TreeItemNVM3 pItem, int trigger) {
+        // TODO:
+        // return true;
+        if ((trigger & TRIGGER_WITHOUTCHANGE) != 0)
+            return false;
+        if (changeless)
+            return false;
+        return true;
+    }
+
+    private TreeItemNVM3 currentTopItem;
+
+    TreeItemNVM3 buildItem(Node n, TreeItemNVM3 pItem) {
+        currentTopItem = pItem;
+        TreeItemNVM3 item = AutoTranslator.translate(this, pItem, n);
+        if (pItem != null)
+            pItem.markRefreshedChild();
+        return item;
+    }
+
+    TreeItemNVM3 buildItemContinued(Node n, TreeItemNVM3 item) {
+        currentTopItem = null;
+        TreeItemNVM3 newItem = AutoTranslator.translateContinued(this, item, n);
+        if (newItem != null)
+            newItem.markRefreshedChild();
+        return newItem;
+    }
+
+    private NVM3Metadata[] topMds;
+
+    private NVM3Metadata[] initMetadataMap(NVM3Metadata[] mds, Node n) {
+        if (topMds != null) return topMds;
+
+        metadataMap = new HashMap<Node, ArrayList<NVM3Metadata>>();
+        ArrayList<NVM3Metadata> mdList = new ArrayList<NVM3Metadata>();
+        for (int i = 0; i < mds.length; i++) {
+            if (mds[i].hasTargets()) {
+                registMetadata(metadataMap, mds[i], n);
+            } else {
+                mdList.add(mds[i]);
+            }
+        }
+        topMds = mdList.toArray(new NVM3Metadata[mdList.size()]);
+        return topMds;
+    }
+
+    public TreeItemNVM3 generateItem(TreeItemNVM3 pItem, Node n) {
+        NVM3Metadata[] cmds = null;
+
+        NVM3Metadata[] mds = baseMetadata.getChildMetadata();
+        if (mds != null) {
+            if (currentTopItem == pItem) {
+                // This item is top.
+                cmds = initMetadataMap(mds, n);
+            } else {
+                initMetadataMap(mds, n);
+                ArrayList<NVM3Metadata> aMeta = metadataMap.get(n);
+                if (aMeta != null) {
+                    cmds = (NVM3Metadata[]) aMeta.toArray(new NVM3Metadata[aMeta.size()]);
+                }
+            }
+        }
+        NVM3Metadata md = NVM3GeneratedMetadata.generate(baseMetadata, this, n, cmds);
+
+        return TreeItemNVM3.newTreeItem(md, pItem, n);
+    }
+
+    private void registMetadata(HashMap<Node, ArrayList<NVM3Metadata>> metaMap, NVM3Metadata meta, Node base) {
+        NodeList list = meta.query(base);
+        
+        for (int i = 0; i < list.getLength(); i++) {
+            Node node = list.item(i);
+            ArrayList<NVM3Metadata> aMeta = metaMap.get(node);
+            if (aMeta == null) {
+                aMeta = new ArrayList<NVM3Metadata>();
+                metaMap.put(node, aMeta);
+            }
+            if (meta instanceof NVM3BundleMetadata) {
+                NVM3Metadata[] m = ((NVM3BundleMetadata) meta).childMetadata;
+                for (int j = 0; j < m.length; j++) {
+                    if (m[j].hasTargets()) {
+                        registMetadata(metaMap, m[j], node);
+                    } else {
+                        aMeta.add(m[j]);
+                    }
+                }
+            }
+        }
+    }
+
+    private List autoAttach(TreeItemNVM3 pItem, NodeList nl, int len, int trigger) throws NVM3Exception {
+        List l = new ArrayList(len);
+
+        for (int i = 0; i < len; i++) {
+            Node n = nl.item(i);
+            /*
+             if (n instanceof CacheableNode) {
+             CachableNode cn = (CachableNode) cn;
+             if (!cn.isChanged()) {
+             } else {
+             }
+             }
+             */
+            TreeItemNVM3 newItem = buildItem(n, pItem);
+            if (newItem != null)
+                l.add(newItem);
+        }
+        if (l.size() == 0) {
+            if (this.trigger == TRIGGER_CLICK) {
+                throw new NVM3InterruptedException("Could not attach the expected nodes.");
+            }
+            return null;
+        }
+        if (waitContents && (l.size() == 0)) {
+            throw new NVM3InterruptedException("Contents have not been prepared yet.");
+        }
+        return l;
+    }
+
+    private List manualAttach(TreeItemNVM3 pItem, NodeList nl, int len, int trigger) throws NVM3Exception {
+        List l = new ArrayList(len);
+
+        NVM3Metadata[] childMds = baseMetadata.getChildMetadata();
+        for (int j = 0; j < len; j++) {
+            for (int i = 0; i < childMds.length; i++) {
+                NVM3Metadata md = childMds[i];
+                List l2 = md.buildItems(pItem, nl.item(j), trigger);
+                if (l2 != null) {
+                    if (l2.size() > 0) {
+                        l.addAll(l2);
+                    } else if (waitContents) {
+                        throw new NVM3InterruptedException("Contents have not been prepared yet.");
+                    }
+                }
+            }
+        }
+
+        return l;
+    }
+
+    private List unwrap(TreeItemNVM3 pItem, NodeList nl, int len, int trigger) {
+        //TODO
+        return null;
+    }
+
+    // TODO:
+    private HashMap cachedResult = new HashMap();
+
+    List expand(TreeItemNVM3 pItem, Node baseNode, int trigger) throws NVM3Exception {
+        if ((trigger != TRIGGER_KEEP) && ((trigger & this.trigger) == 0)) {
+            return null;
+        }
+        Object key;
+        if (baseNode != null) {
+            key = baseNode;
+        } else if (pItem == null) {
+            key = null;
+        } else {
+            key = pItem.getBaseNode();
+        }
+        if (!changed(pItem, trigger)) {
+            List l = (List) cachedResult.get(key);
+            if (l != null)
+                return l;
+        }
+
+        NodeList nl;
+        if (baseNode != null) {
+            nl = baseMetadata.query(baseNode);
+        } else {
+            nl = baseMetadata.query(pItem);
+        }
+        int len = nl.getLength();
+        List result = null;
+        switch (type) {
+        case TYPE_ATTACH:
+            if (len == 0) {
+                if (this.trigger == TRIGGER_CLICK) {
+                    throw new NVM3InterruptedException("Could not attach the expected nodes.");
+                }
+                return null;
+            }
+            if (automatic) {
+                result = autoAttach(pItem, nl, len, trigger);
+            } else {
+                result = manualAttach(pItem, nl, len, trigger);
+            }
+            cachedResult.put(key, result);
+            return result;
+        case TYPE_UNWRAP:
+            // TODO;
+        }
+
+        return result;
+    }
+
+    NVM3Mode(int type, int trigger, boolean automatic, boolean changeless, boolean waitContents) {
+        this.type = type;
+        this.trigger = trigger;
+        this.automatic = automatic;
+        this.changeless = changeless;
+        this.waitContents = waitContents;
+    }
+
+    NVM3Mode(int type) {
+        this.type = type;
+        this.trigger = TRIGGER_ALWAYS;
+        this.automatic = false;
+        this.changeless = false;
+        this.waitContents = false;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3RecombinantMetadata.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3RecombinantMetadata.java
new file mode 100644
index 0000000..e5fcd4e
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3RecombinantMetadata.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.impl;
+
+import java.util.List;
+
+import org.eclipse.actf.ai.fennec.NVM3Exception;
+import org.eclipse.actf.ai.query.IQuery;
+import org.w3c.dom.Node;
+
+
+class NVM3RecombinantMetadata extends NVM3BundleMetadata {
+    static public final int TYPE_SIMPLE = 0;
+    static public final int TYPE_ATTACH = 1;
+    static public final int TYPE_UNWRAP = 2;
+
+    private final int type;
+    private NVM3Mode nextMode;
+
+    public NVM3Mode getNextMode() {
+        return nextMode;
+    }
+
+    List expand(TreeItemNVM3 pItem, int trigger) throws NVM3Exception {
+        return nextMode.expand(pItem, null, trigger);
+    }
+
+    List buildItems(TreeItemNVM3 baseItem, Node baseNode, int trigger) throws NVM3Exception {
+        return nextMode.expand(baseItem, baseNode, trigger);
+    }
+
+    TreeItemNVM3 buildRootItem() throws NVM3Exception {
+        TreeItemNVM3 root = super.buildRootItem();
+        if (root == null) {
+            throw new NVM3Exception("NVM3.EMPTY_PAGE", null);            
+        }
+        return root;
+    }
+
+
+    static NVM3RecombinantMetadata newAttach(NVM3ServiceImpl nvm3Service,
+                                             IQuery q, NVM3Mode mode,
+                                             NVM3Mode nextMode,
+                                             NVM3Metadata[] mds) {
+        NVM3RecombinantMetadata md = new NVM3RecombinantMetadata(nvm3Service,
+                                                                 q, mode,
+                                                                 nextMode, mds,
+                                                                 TYPE_ATTACH);
+        return md;
+    }
+
+    static NVM3RecombinantMetadata newUnwrap(NVM3ServiceImpl nvm3Service,
+                                             IQuery q, NVM3Mode mode,
+                                             NVM3Mode nextMode,
+                                             NVM3Metadata[] mds) {
+        NVM3RecombinantMetadata md = new NVM3RecombinantMetadata(nvm3Service,
+                                                                 q, mode,
+                                                                 nextMode, mds,
+                                                                 TYPE_UNWRAP);
+        return md;
+    }
+
+    private NVM3RecombinantMetadata(NVM3ServiceImpl nvm3Service,
+                                    IQuery q,
+                                    NVM3Mode mode,
+                                    NVM3Mode nextMode,
+                                    NVM3Metadata[] mds,
+                                    int type) {
+        super(nvm3Service, q, mode, mds);
+        this.type = type;
+        this.nextMode = nextMode;
+        nextMode.setBaseMetadata(this);
+    }
+}
+
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3ServiceImpl.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3ServiceImpl.java
new file mode 100644
index 0000000..484849d
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3ServiceImpl.java
@@ -0,0 +1,290 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.impl;
+
+import org.eclipse.actf.ai.fennec.INVM3Entry;
+import org.eclipse.actf.ai.fennec.INVM3Service;
+import org.eclipse.actf.ai.fennec.NVM3Exception;
+import org.eclipse.actf.ai.fennec.NVM3InterruptedException;
+import org.eclipse.actf.ai.fennec.treemanager.IAccessKeyList;
+import org.eclipse.actf.ai.fennec.treemanager.ISoundControl;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeManager;
+import org.eclipse.actf.ai.fennec.treemanager.IVideoControl;
+import org.eclipse.actf.ai.fennec.treemanager.TreeManagerException;
+import org.eclipse.actf.ai.query.IQuery;
+import org.eclipse.actf.ai.query.QueryService;
+import org.eclipse.actf.model.dom.dombycom.AnalyzedResult;
+import org.eclipse.actf.model.dom.dombycom.IDocumentEx;
+import org.eclipse.actf.model.dom.dombycom.IFlashNode;
+import org.eclipse.actf.model.dom.dombycom.INodeEx;
+import org.eclipse.actf.util.vocab.IProposition;
+import org.eclipse.actf.util.vocab.Vocabulary;
+import org.w3c.dom.Element;
+
+
+public class NVM3ServiceImpl implements INVM3Service {
+    private NVM3Metadata rootMetadata;
+
+    private int status;
+
+    private TreeItemNVM3 lastItem;
+
+    private IDocumentEx document;
+
+    private Element root;
+
+    private AnalyzedResult analyzedResult;
+
+    Element getDocumentElement() {
+        return root;
+    }
+
+    private void initDefaultMetadata() {
+        NVM3Mode rootMode = new NVM3Mode(NVM3Mode.TYPE_SIMPLE);
+        NVM3Mode nextMode = new NVM3Mode(NVM3Mode.TYPE_ATTACH, NVM3Mode.TRIGGER_ALWAYS, true, false, false);
+        // /HTML/FRAMESET/FRAME/HTML/BODY
+        //IQuery q = QueryService.createFromXPath("/HTML/BODY|/HTML/FRAMESET//FRAME/HTML/BODY");
+        //IQuery q = QueryService.createFromXPath("//HTML/BODY");
+        IQuery q = QueryService.createFromXPath("/HTML");
+        NVM3Metadata[] mds = new NVM3Metadata[1];
+        mds[0] = NVM3RecombinantMetadata.newAttach(this, q, rootMode, nextMode, null);
+        rootMetadata = new NVM3GroupMetadata(this, null, rootMode, mds);
+    }
+
+    public NVM3ServiceImpl(INVM3Entry entry, IDocumentEx document) throws NVM3Exception {
+        this.document = document;
+        this.root = document.getDocumentElement();
+        try {
+            NVM3DOMReader reader = new NVM3DOMReader(this);
+            rootMetadata = reader.parse(entry);
+            this.status = UNINIT;
+        } catch (Exception e) {
+            throw new NVM3Exception("Failed to load NVM3 data.", e);
+        }
+    }
+
+    public NVM3ServiceImpl(IDocumentEx document) {
+        this.document = document;
+        this.root = document.getDocumentElement();
+        initDefaultMetadata();
+    }
+
+    public int getStatus() {
+        return status;
+    }
+
+    public int analyze() throws NVM3Exception {
+        analyzedResult = new AnalyzedResult();
+        if (root instanceof INodeEx) {
+            cachedVideoControl = null;
+            cachedSoundControl = null;
+            analyzedResult = ((INodeEx) root).analyze(analyzedResult);
+        }
+        return ITreeManager.NOACTION;
+    }
+
+    private ISoundControl cachedSoundControl;
+
+    private IVideoControl cachedVideoControl;
+
+    private IAccessKeyList cachedAccessKeyList;
+    
+    private IFlashNode[] cachedFlashTopNodes;
+
+    public int initialize() throws NVM3Exception {
+        if (analyzedResult == null) {
+            analyze();
+        }
+        lastItem = rootMetadata.buildRootItem();
+        if (lastItem == null) {
+            throw new NVM3Exception("Failed to initialize", null);
+        }
+        status = NORMAL;
+        return ITreeManager.MOVED;
+    }
+
+    public ITreeItem getLastTreeItem() {
+        return lastItem;
+    }
+
+    private int update(ITreeItem target, int trigger) throws NVM3Exception {
+        TreeItemNVM3 targetItem = (TreeItemNVM3) target;
+        if (targetItem.getParent() == null) {
+            return initialize();
+        } else {
+            targetItem = targetItem.expand(trigger);
+            if (targetItem == null) {
+                return ITreeManager.NOACTION;
+            }
+        }
+        if (targetItem == null) {
+            status = UNINIT;
+            throw new NVM3InterruptedException("Lost my way"); // $ NON-NLS-1
+        }
+        lastItem = targetItem;
+        return ITreeManager.MOVED;
+    }
+
+    public int moveUpdate(ITreeItem target) throws NVM3Exception {
+        return update(target, NVM3Mode.TRIGGER_MOVE);
+    }
+
+    public int moveUpdate(ITreeItem target, boolean update) throws NVM3Exception {
+        if (update) {
+            return update(target, NVM3Mode.TRIGGER_MOVE);
+        } else {
+            return update(target, NVM3Mode.TRIGGER_MOVE | NVM3Mode.TRIGGER_WITHOUTCHANGE);
+        }
+    }
+
+    public int clickUpdate(ITreeItem target) throws NVM3Exception {
+        return update(target, NVM3Mode.TRIGGER_CLICK) | ITreeManager.CLICKED;
+    }
+
+    private int searchForwardChildren(int idx, TreeItemNVM3 item, IProposition proposition) throws NVM3Exception {
+        int st;
+        item = item.expandChildItems(NVM3Mode.TRIGGER_KEEP);
+        ITreeItem[] childItems = item.getChildItems();
+        for (int i = idx; i < childItems.length; i++) {
+            TreeItemNVM3 cItem = (TreeItemNVM3) childItems[i];
+            if (proposition.eval(cItem)) {
+                return moveUpdate(cItem) | ITreeManager.FOUND;
+            }
+            st = searchForwardChildren(0, cItem, proposition);
+            if ((st & ITreeManager.FOUND) != 0)
+                return st;
+        }
+        return ITreeManager.NOACTION;
+    }
+
+    private int searchForwardInternal(int idx, TreeItemNVM3 item, IProposition proposition) throws NVM3Exception {
+        int st = searchForwardChildren(idx, item, proposition);
+        if ((st & ITreeManager.FOUND) != 0)
+            return st;
+        TreeItemNVM3 pItem = (TreeItemNVM3) item.getParent();
+        if (pItem == null)
+            return ITreeManager.NOACTION;
+        st = searchForwardInternal(item.getNth() + 1, pItem, proposition);
+        if ((st & ITreeManager.FOUND) != 0)
+            return st;
+
+        return ITreeManager.NOACTION;
+    }
+
+    public ITreeItem[] getSiblings() throws TreeManagerException {
+        ITreeItem parent = lastItem.getParent();
+        if (parent == null) {
+            ITreeItem[] itas = new ITreeItem[1];
+            itas[0] = lastItem;
+            return itas;
+        }
+        return parent.getChildItems();
+    }
+
+    public int searchForward(IProposition proposition) throws NVM3Exception {
+        return searchForwardInternal(0, lastItem, proposition);
+    }
+
+    private int searchBackwardInternal(boolean first, TreeItemNVM3 item, IProposition proposition) throws NVM3Exception {
+        TreeItemNVM3 pItem = (TreeItemNVM3) item.getParent();
+        if (pItem == null)
+            return ITreeManager.NOACTION;
+        int nth = item.getNth() - 1;
+        if (nth < 0) {
+            if (proposition.eval(pItem)) {
+                return moveUpdate(pItem) | ITreeManager.FOUND;
+            }
+            return searchBackwardInternal(false, pItem, proposition);
+        }
+        if (first) {
+            pItem = pItem.expandChildItems(NVM3Mode.TRIGGER_KEEP);
+        }
+        ITreeItem[] siblings = pItem.getChildItems();
+        if (nth >= siblings.length) {
+            nth = siblings.length - 1;
+        }
+        item = (TreeItemNVM3) siblings[nth];
+
+        while (true) {
+            item = item.expandChildItems(NVM3Mode.TRIGGER_KEEP);
+            ITreeItem[] childItems = item.getChildItems();
+            if (childItems.length == 0)
+                break;
+            item = (TreeItemNVM3) childItems[childItems.length - 1];
+        }
+        if (proposition.eval(item)) {
+            return moveUpdate(item) | ITreeManager.FOUND;
+        }
+        return searchBackwardInternal(false, item, proposition);
+    }
+
+    public int searchBackward(IProposition predicate) throws NVM3Exception {
+        return searchBackwardInternal(true, lastItem, predicate);
+    }
+
+    public ISoundControl getSoundControl() {
+        if (cachedSoundControl == null) {
+            cachedSoundControl = TreeItemSoundControl.newTreeItemSoundControl(analyzedResult);
+        }
+        return cachedSoundControl;
+    }
+
+    public IVideoControl getVideoControl() {
+        if (cachedVideoControl == null) {
+            cachedVideoControl = TreeItemVideoControl.newTreeItemVideoControl(analyzedResult, this);
+        }
+        return cachedVideoControl;
+    }
+    
+    public IAccessKeyList getAccessKeyList() {
+        if (cachedAccessKeyList == null) {
+            cachedAccessKeyList = TreeItemAccessKeyList.newAccessKeyList(analyzedResult);
+        }
+        return cachedAccessKeyList;
+    }
+
+    public IFlashNode[] getFlashTopNodes() {
+        if (cachedFlashTopNodes == null) {
+            cachedFlashTopNodes = analyzedResult.getFlashTopNodes();
+        }
+        return cachedFlashTopNodes;
+    }
+
+    private void expandWholeTreeInternal(TreeItemNVM3 item) throws NVM3Exception {
+        item = item.expandChildItems(NVM3Mode.TRIGGER_KEEP);
+        ITreeItem[] childItems = item.getChildItems();
+        for (int i = 0; i < childItems.length; i++) {
+            expandWholeTreeInternal((TreeItemNVM3) childItems[i]);
+        }
+    }
+
+    private int skipToNode(Element e) throws NVM3Exception {
+        initialize();
+        return searchForward(Vocabulary.nodeLocation(e, false));
+    }
+
+    public int skipToAnchor(String target) throws NVM3Exception {
+        Element el = document.getTargetElement(target);
+        if (el != null)
+            return skipToNode(el);
+        return ITreeManager.NOACTION;
+    }
+
+    public ITreeItem expandWholeTree() throws NVM3Exception {
+        initialize();
+        if (lastItem instanceof TreeItemNVM3) {
+            expandWholeTreeInternal((TreeItemNVM3) lastItem);
+        }
+        return lastItem;
+    }
+
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3SimpleMetadata.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3SimpleMetadata.java
new file mode 100644
index 0000000..88efca3
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3SimpleMetadata.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.impl;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.actf.ai.fennec.NVM3Exception;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.query.IQuery;
+import org.w3c.dom.NodeList;
+
+
+class NVM3SimpleMetadata extends NVM3Metadata {
+    static public final int TYPE_ALT_TEXT = 1;
+    static public final int TYPE_DESCRIPTION = 2;
+    static public final int TYPE_HEADER = 3;
+
+    private short headingLevel;
+    private int type;
+
+    private List stringFormat;
+
+    public short getHeadingLevel(ITreeItem item) {
+        return headingLevel;
+    }
+    
+    public String getString(ITreeItem item) {
+        StringBuffer buf = new StringBuffer();
+        if (stringFormat == null) return null;
+        Iterator it = stringFormat.iterator();
+        while (it.hasNext()) {
+            Object o = it.next();
+            if (o instanceof String) {
+                buf.append((String) o);
+            } else if (o instanceof IQuery) {
+                IQuery q = (IQuery) o;
+                NodeList nl = query(q, item);
+                String str = NodeUtil.extractString(nl);
+                buf.append(str);
+            }
+        }
+        return buf.toString();
+    }
+
+    public String getAltText(ITreeItem item) {
+        if (type == TYPE_ALT_TEXT) return getString(item);
+        return "";
+    }
+
+    public String getDescription(ITreeItem item) {
+        if (type == TYPE_DESCRIPTION) return getString(item);
+        return "";
+    }
+
+    static NVM3SimpleMetadata newHeader(NVM3ServiceImpl nvm3Service,
+                                        IQuery q, NVM3Mode mode, short level) {
+        NVM3SimpleMetadata md = new NVM3SimpleMetadata(nvm3Service, q, mode);
+        md.type = TYPE_HEADER;
+        md.headingLevel = level;
+        return md;
+    }
+
+    static NVM3SimpleMetadata newAltText(NVM3ServiceImpl nvm3Service,
+                                         IQuery q, NVM3Mode mode, List stringFormat) {
+        NVM3SimpleMetadata md = new NVM3SimpleMetadata(nvm3Service, q, mode);
+        md.type = TYPE_ALT_TEXT;
+        md.stringFormat = stringFormat;
+        return md;
+    }
+
+    static NVM3SimpleMetadata newDescription(NVM3ServiceImpl nvm3Service,
+                                             NVM3Mode mode, IQuery q, List stringFormat) {
+        NVM3SimpleMetadata md = new NVM3SimpleMetadata(nvm3Service, q, mode);
+        md.type = TYPE_DESCRIPTION;
+        md.stringFormat = stringFormat;
+        return md;
+    }
+
+    private NVM3SimpleMetadata(NVM3ServiceImpl nvm3Service,
+                               IQuery q, NVM3Mode mode) {
+        super(nvm3Service, q, mode);
+    }
+
+    List expand(TreeItemNVM3 pItem, int trigger) throws NVM3Exception {
+        return null;
+    }
+}
+
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3TableMetadata.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3TableMetadata.java
new file mode 100644
index 0000000..1c3229e
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3TableMetadata.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.impl;
+
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.query.IQuery;
+import org.eclipse.actf.model.dom.dombycom.INodeEx;
+
+
+class NVM3TableMetadata extends NVM3BundleMetadata {
+    private final NVM3TableRowMetadata[] rowMds;
+
+    public int getRowSize() {
+        return rowMds.length;
+    }
+
+    public NVM3TableRowMetadata getRow(int idx) {
+        return rowMds[idx];
+    }
+    
+    public String getAltText(ITreeItem item) {
+        String r = super.getAltText(item);
+        if (r.length() > 0) return r;
+        // If not specified, extract some string from the node.
+        return NodeUtil.extractString(item);
+    }
+
+    public short getHeadingLevel(ITreeItem item) {
+        short r = super.getHeadingLevel(item);
+        if (r > 0) return r;
+        if (r == -1) return -1;
+        Object baseNode = item.getBaseNode();
+        if (baseNode instanceof INodeEx) {
+            return ((INodeEx) baseNode).getHeadingLevel();
+        }
+        return 0;
+    }
+
+    NVM3TableMetadata(NVM3ServiceImpl nvm3Service,
+                      IQuery q, NVM3Mode mode,
+                      NVM3Metadata[] childMds,
+                      NVM3TableRowMetadata[] rowMds) {
+        super(nvm3Service, q, mode, childMds);
+        this.rowMds = rowMds;
+    }
+
+}
+
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3TableRowMetadata.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3TableRowMetadata.java
new file mode 100644
index 0000000..01d398f
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NVM3TableRowMetadata.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.impl;
+
+import org.w3c.dom.Node;
+
+class NVM3TableRowMetadata extends NVM3BundleMetadata {
+
+	protected NVM3TableRowMetadata(NVM3ServiceImpl nvm3Service,
+                                       NVM3Mode mode,
+                                       Node node,
+                                       NVM3Metadata[] mds) {
+		super(nvm3Service, mode, node, mds);
+	}
+}
+
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NodeUtil.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NodeUtil.java
new file mode 100644
index 0000000..e65e3a1
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/NodeUtil.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.impl;
+
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.model.dom.dombycom.INodeEx;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+
+
+
+public class NodeUtil {
+    private static StringBuffer extractNodeString(StringBuffer buf, Node n) {
+        if (n instanceof INodeEx) {
+            INodeEx n2 = (INodeEx) n;
+            
+            // TODO: should be rewritten!!
+            // changed by daisuke
+            String n2String = n2.extractString();
+            buf.append(n2String);
+            if (n2String != null && n2String.length() > 0) // if node has text
+                buf.append(" "); // insert default separator
+        }
+        NodeList nl = n.getChildNodes();
+        int len = nl.getLength();
+        for (int i = 0; i < len; i++) {
+            buf = extractNodeString(buf, nl.item(i));
+        }
+        return buf;
+    }
+
+    public static String extractString(NodeList nl) {
+        StringBuffer buf = new StringBuffer();
+        int len = nl.getLength();
+        for (int i = 0; i < len; i++) {
+            Node n = nl.item(i);
+            buf = extractNodeString(buf, n);
+        }
+        return buf.toString();
+    }
+
+    public static String extractString(Node n) {
+        StringBuffer buf = new StringBuffer();
+        buf = extractNodeString(buf, n);
+        return buf.toString();
+    }
+
+    public static String extractString(ITreeItem item) {
+        Node baseNode = (Node) item.getBaseNode();
+        if (baseNode != null) {
+            return extractString(baseNode);
+        }
+        return "";
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TableImpl.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TableImpl.java
new file mode 100644
index 0000000..7e55986
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TableImpl.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+
+package org.eclipse.actf.ai.fennec.impl;
+
+import org.eclipse.actf.ai.fennec.treemanager.ITable;
+import org.eclipse.actf.ai.fennec.treemanager.ITableRow;
+
+
+
+public class TableImpl implements ITable {
+    private NVM3TableMetadata tableMetadata;
+
+    public ITableRow[] getRow(int row) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public int getColumnSize() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int getRowSize() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public String getTableString() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemAccessKeyList.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemAccessKeyList.java
new file mode 100644
index 0000000..b8d22c8
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemAccessKeyList.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.impl;
+
+import org.eclipse.actf.ai.fennec.treemanager.IAccessKeyList;
+import org.eclipse.actf.model.dom.dombycom.AnalyzedResult;
+import org.eclipse.actf.model.dom.dombycom.INodeEx;
+
+
+
+
+public class TreeItemAccessKeyList implements IAccessKeyList {
+    INodeEx[] accessKeyNodes;
+
+    public TreeItemAccessKeyList(AnalyzedResult analyzedResult) {
+        accessKeyNodes = analyzedResult.getAccessKeyNodes();
+    }
+
+    public char getAccessKeyAt(int index) {
+        if (accessKeyNodes.length <= index)
+            return 0;
+        return accessKeyNodes[index].getAccessKey();
+    }
+
+    public String getUIStringAt(int index) {
+        // TODO
+        return "";
+    }
+
+    public int size() {
+        return accessKeyNodes.length;
+    }
+
+    public static IAccessKeyList newAccessKeyList(AnalyzedResult analyzedResult) {
+        return new TreeItemAccessKeyList(analyzedResult);
+    }
+
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemMark.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemMark.java
new file mode 100644
index 0000000..c6e634f
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemMark.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.impl;
+
+import java.util.List;
+
+import org.eclipse.actf.ai.fennec.NVM3Exception;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.util.vocab.AbstractTerms;
+import org.eclipse.actf.util.vocab.IEvalTarget;
+
+
+
+
+public class TreeItemMark extends TreeItemNVM3 {
+    private static class TreeItemMarkTerms extends TreeItemTerms {
+        @Override
+        public boolean hasContent(IEvalTarget target) {
+            return true;
+        }
+
+        @Override
+        public boolean hasReadingContent(IEvalTarget target) {
+            return true;
+        }
+
+        @Override
+        public boolean isFlashLastNode(IEvalTarget node) {
+            if (node instanceof TreeItemMark) {
+                TreeItemMark mark = (TreeItemMark) node;
+                if (mark.getType() == MarkType.FLASH_END) 
+                    return true;
+            }
+            return false;
+        }
+
+        TreeItemMarkTerms() {
+            super(null);
+        }
+    }
+
+    private static TreeItemTerms treeItemMarkTermsInstance = new TreeItemMarkTerms();
+
+    public enum MarkType {
+        FLASH_END
+    }
+
+    @Override
+    public AbstractTerms getTerms() {
+        return treeItemMarkTermsInstance;
+    }
+    
+    private MarkType type;
+    
+    public TreeItemMark(ITreeItem parent, MarkType type) {
+        super(null, parent, null, treeItemMarkTermsInstance);
+        this.type = type;
+    }
+    
+    @Override
+    public String getUIString() {
+        return "";
+    }
+    
+    @Override
+    public String getNodeString() {
+        return "Mark";
+    }
+    
+    public MarkType getType() {
+        return type;
+    }
+    
+    @Override
+    TreeItemNVM3 expandChildItems(int trigger) throws NVM3Exception {
+        setChildItems((List) null);
+        return this;
+    }
+
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemNVM3.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemNVM3.java
new file mode 100644
index 0000000..3868efc
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemNVM3.java
@@ -0,0 +1,490 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.impl;
+
+import java.util.List;
+
+import org.eclipse.actf.ai.fennec.NVM3Exception;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeManager;
+import org.eclipse.actf.ai.fennec.treemanager.TreeManagerException;
+import org.eclipse.actf.ai.query.QueryService;
+import org.eclipse.actf.model.dom.dombycom.IElementEx;
+import org.eclipse.actf.model.dom.dombycom.INodeEx;
+import org.eclipse.actf.model.dom.dombycom.ISelectElement;
+import org.eclipse.actf.model.dom.dombycom.IElementEx.Position;
+import org.eclipse.actf.util.vocab.AbstractTerms;
+import org.eclipse.actf.util.vocab.IEvalTarget;
+import org.eclipse.actf.util.vocab.Vocabulary;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+
+// TODO I'd like to make it package-local.
+public class TreeItemNVM3 implements ITreeItem {
+    static private final TreeItemNVM3[] emptyChild = new TreeItemNVM3[0];
+
+    private Node baseNode;
+
+    private ITreeItem parent;
+
+    private ITreeItem[] childItems;
+
+    private NVM3Metadata metadata;
+
+    private boolean hasAlreadyChildRefreshed;
+
+    private int nth;
+
+    private TreeItemTerms terms;
+
+    // for terms
+    int distance = 0;
+    
+    public static TreeItemNVM3 newTreeItem(NVM3Metadata metadata, ITreeItem parent, Node baseNode) {
+        if (baseNode == null) {
+            // TODO
+            return new TreeItemNVM3(metadata, parent, null, new TreeItemTerms(null));
+        }
+        if (!(baseNode instanceof IEvalTarget)) return null;
+        IEvalTarget evalTarget = (IEvalTarget) baseNode;
+        return new TreeItemNVM3(metadata, parent, baseNode, new TreeItemTerms(evalTarget)); 
+    }
+
+    protected TreeItemNVM3(NVM3Metadata metadata,
+                           ITreeItem parent, Node baseNode,
+                           TreeItemTerms terms) {
+        this.terms = terms;
+        this.metadata = metadata;
+        this.parent = parent;
+        this.baseNode = baseNode;
+        this.childItems = emptyChild;
+        this.nth = 0;
+    }
+    
+
+    public AbstractTerms getTerms() {
+        return terms;
+    }
+
+    public ITreeItem getParent() {
+        return parent;
+    }
+
+    public int getNth() {
+        return nth;
+    }
+
+    private void setAsRoot() {
+        this.parent = null;
+    }
+
+    private void setParent() {
+        for (int i = 0; i < childItems.length; i++) {
+            TreeItemNVM3 c = (TreeItemNVM3) childItems[i];
+            c.parent = this;
+            c.nth = i;
+        }
+    }
+
+    public void setChildItems(List items) {
+        if (items == null) {
+            this.childItems = emptyChild;
+        } else {
+            this.childItems = (ITreeItem[]) items.toArray(emptyChild);
+        }
+        setParent();
+    }
+
+    public void setChildItems(ITreeItem[] items) {
+        if (items == null) {
+            this.childItems = emptyChild;
+        } else {
+            this.childItems = items;
+        }
+        setParent();
+    }
+
+    public void appendChildItems(ITreeItem[] items) {
+        if (items == null)
+            return;
+        ITreeItem[] newItems = new ITreeItem[childItems.length + items.length];
+        int i, j;
+        for (i = 0; i < childItems.length; i++) {
+            newItems[i] = childItems[i];
+        }
+        for (j = 0; j < items.length; i++, j++) {
+            TreeItemNVM3 c = (TreeItemNVM3) items[j];
+            c.parent = this;
+            newItems[i] = c;
+        }
+        this.childItems = newItems;
+    }
+
+    public boolean hasChild() {
+        return (childItems.length > 0);
+    }
+
+    public void forceParent(ITreeItem parent) {
+        this.parent = parent;
+    }
+
+    void markRefreshedChild() {
+        hasAlreadyChildRefreshed = true;
+    }
+
+    boolean hasAlreadyChildRefreshed() {
+        return hasAlreadyChildRefreshed;
+    }
+
+    TreeItemNVM3 expandChildItems(int trigger) throws NVM3Exception {
+        List newChildItems = metadata.expand(this, trigger);
+        setChildItems(newChildItems);
+        return this;
+    }
+
+    private Node getNearestNodeInternal(int idx) {
+        if (baseNode != null) return baseNode;
+        for (; idx < childItems.length; idx++) {
+            TreeItemNVM3 item = (TreeItemNVM3) childItems[idx];
+            return item.getNearestNodeInternal(0);
+        }
+        if (parent == null) return null;
+        return ((TreeItemNVM3) parent).getNearestNodeInternal(getNth());
+    }
+
+    Node getNearestNode() {
+        return getNearestNodeInternal(0);
+    }
+
+    private TreeItemNVM3 autoUnwrap(int trigger) throws NVM3Exception {
+        TreeItemNVM3 item = this;
+
+        for (;;) {
+            TreeItemNVM3 parent = (TreeItemNVM3) item.getParent();
+            if (parent == null) {
+                item.setChildItems(emptyChild);
+                return item;
+            }
+            int idx = item.getNth();
+            List newSiblings = parent.metadata.expand(parent, (NVM3Mode.TRIGGER_KEEP
+                                                               | NVM3Mode.TRIGGER_UNWRAP));
+            if (newSiblings != null) {
+                int newSize = newSiblings.size();
+                if (newSize > 0) {
+                    if (idx >= newSize) idx = newSize - 1;
+                    item = (TreeItemNVM3) newSiblings.get(idx);
+
+                    item = item.expandChildItems(trigger);
+                    // success. Set parent's childItems.
+                    parent.setChildItems(newSiblings);
+                    return item;
+                }
+            }
+            item = parent;
+        }
+    }
+
+    TreeItemNVM3 expand(int trigger) throws NVM3Exception {
+        TreeItemNVM3 parent = (TreeItemNVM3) getParent();
+        if (parent != null) {
+            int idx = getNth();
+            List newSiblings = parent.metadata.expand(parent, NVM3Mode.TRIGGER_KEEP);
+            if (newSiblings == null) {
+                return parent.autoUnwrap(trigger);
+            }
+            int newSize = newSiblings.size();
+            if (newSize == 0) {
+                return parent.autoUnwrap(trigger);
+            }
+            if (idx >= newSize) {
+                idx = newSize - 1;
+            }
+            TreeItemNVM3 newTarget = (TreeItemNVM3) newSiblings.get(idx);
+            newTarget = newTarget.expandChildItems(trigger);
+            // success. Set parent's childItems.
+            parent.setChildItems(newSiblings);
+            return newTarget;
+        } else {
+            List newChildItems = metadata.expand(this, trigger);
+            setChildItems(newChildItems);
+            this.setAsRoot();
+            return this;
+        }
+    }
+
+    public ITreeItem[] getChildItems() {
+        return childItems;
+    }
+
+    public String getUIString() {
+        if (metadata != null) {
+            String r = metadata.getAltText(this);
+            if (r == null)
+                return "";
+            if (r.length() > 0)
+                return r;
+        }
+        if (baseNode instanceof INodeEx) {
+            INodeEx node2 = (INodeEx) baseNode;
+            return node2.extractString();
+        }
+        return "";
+    }
+
+    public String getDescription() {
+        if (metadata != null) {
+            String r = metadata.getDescription(this);
+            if (r == null)
+                return "";
+            if (r.length() > 0)
+                return r;
+        }
+        return "";
+    }
+
+    public String getNodeString() {
+        if (baseNode != null) {
+            return baseNode.getNodeName();
+        }
+        return "No Node";
+    }
+
+    public short getHeadingLevel() {
+        if (metadata != null)
+            return metadata.getHeadingLevel(this);
+        else
+            return 0;
+    }
+
+    public String getLinkURI() {
+        if (baseNode instanceof INodeEx) {
+            INodeEx node2 = (INodeEx) baseNode;
+            return node2.getLinkURI();
+        }
+        return null;
+    }
+
+    public Object getBaseNode() {
+        return baseNode;
+    }
+    
+    public NVM3Metadata getMetadata() {
+        return metadata;
+    }
+
+    public int doClick() throws TreeManagerException {
+        if (baseNode instanceof INodeEx) {
+            ((INodeEx) baseNode).doClick();
+            return ITreeManager.CLICKED;
+        }
+        return ITreeManager.NOACTION;
+    }
+
+    public int stay() throws TreeManagerException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int highlight() throws TreeManagerException {
+        if (baseNode instanceof INodeEx) {
+            ((INodeEx) baseNode).highlight();
+        }
+        return ITreeManager.NOACTION;
+    }
+
+    public int unhighlight() throws TreeManagerException {
+        if (baseNode instanceof INodeEx) {
+            ((INodeEx) baseNode).unhighlight();
+        }
+        return ITreeManager.NOACTION;
+    }
+
+    public boolean setFocus() {
+        if (baseNode instanceof INodeEx) {
+            ((INodeEx) baseNode).setFocus();
+        }
+        return false;
+    }
+
+    // !FN!
+    public boolean isInputable() {
+        if (baseNode instanceof IEvalTarget) {
+            return Vocabulary.isInputable().eval((IEvalTarget) baseNode);
+        }
+        return false;
+    }
+
+    // !FN!
+    public boolean isClickable() {
+        if (baseNode instanceof IEvalTarget) {
+            return Vocabulary.isClickable().eval((IEvalTarget) baseNode);
+        }
+        return false;
+    }
+
+    // !FN!
+    public boolean isImage() {
+        if (baseNode instanceof IEvalTarget) {
+            return Vocabulary.isImage().eval((IEvalTarget) baseNode);
+        }
+        return false;
+    }
+
+    // !FN!
+    public String[] getStillPictureData() {
+        if (!(baseNode instanceof INodeEx)) {
+            return new String[3];
+        }
+        return ((INodeEx) baseNode).getStillPictureData();
+    }
+
+    public int setText(String text) throws TreeManagerException {
+        if (baseNode instanceof INodeEx) {
+            ((INodeEx) baseNode).setText(text);
+        }
+        return ITreeManager.NOACTION;
+    }
+
+    public String getText() throws TreeManagerException {
+        if (baseNode instanceof INodeEx) {
+            return ((INodeEx) baseNode).getText();
+        }
+        return "";
+    }
+
+    public void addMetadata(TreeItemNVM3 item) {
+        if (!(this.metadata instanceof NVM3GeneratedMetadata))
+            return;
+        if (!(item.metadata instanceof NVM3GeneratedMetadata))
+            return;
+
+        NVM3GeneratedMetadata meta = (NVM3GeneratedMetadata) this.metadata;
+        NVM3GeneratedMetadata meta2 = (NVM3GeneratedMetadata) item.metadata;
+
+        this.metadata = NVM3GeneratedMetadata.generate(meta, meta2);
+    }
+
+    
+    private Position radioPosition;
+
+    private Position listPosition;
+
+    public int getRadioIndex() {
+        if (baseNode instanceof IElementEx) {
+            if (radioPosition == null)
+                radioPosition = ((IElementEx) baseNode).getRadioPosition();
+            if (radioPosition == null)
+                return 0;
+            return radioPosition.index;
+        }
+        return 0;
+    }
+
+    public int getRadioTotal() {
+        if (baseNode instanceof IElementEx) {
+            if (radioPosition == null)
+                radioPosition = ((IElementEx) baseNode).getRadioPosition();
+            if (radioPosition == null)
+                return 0;
+            return radioPosition.total;
+        }
+        return 0;
+    }
+
+    public int getListIndex() {
+        Node current = baseNode;
+        do {
+            if (current instanceof IElementEx) {
+                if (listPosition == null)
+                    listPosition = ((IElementEx) current).getListPosition();
+                if (listPosition == null)
+                    return 0;
+                return listPosition.index;
+            }
+            current = current.getParentNode();
+        } while (current != null);
+        return 0;
+    }
+
+    public int getListTotal() {
+        Node current = baseNode;
+        do {
+            if (current instanceof IElementEx) {
+                if (listPosition == null)
+                    listPosition = ((IElementEx) current).getListPosition();
+                if (listPosition == null)
+                    return 0;
+                return listPosition.total;
+            }
+            current = current.getParentNode();
+        } while (current != null);
+        return 0;
+    }
+
+    public String getFormLabel() {
+        if (!(baseNode instanceof IElementEx))
+            return "";
+        Element label = ((IElementEx) baseNode).getFormLabel();
+        if (label == null || !(label instanceof INodeEx))
+            return "";
+        NodeList nl = label.getChildNodes();
+        StringBuffer ret = new StringBuffer();
+        for (int i = 0; i < nl.getLength(); i++) {
+            Node node = nl.item(i);
+            if (node instanceof INodeEx)
+                ret.append(((INodeEx) node).extractString());
+        }
+        return ret.toString();
+    }
+
+    public void setSelectedIndices(int[] indices) {
+        if (baseNode instanceof ISelectElement) {
+            ((ISelectElement) baseNode).setSelectedIndices(indices);
+        }
+    }
+
+    public int[] getSelectedIndices() {
+        if (baseNode instanceof ISelectElement) {
+            return ((ISelectElement) baseNode).getSelectedIndices();
+        }
+        return null;
+    }
+
+    public int getOptionsCount() {
+        if (baseNode instanceof ISelectElement) {
+            return ((ISelectElement) baseNode).getOptionsCount();
+        }
+        return 0;
+    }
+
+    public String getOptionTextAt(int index) {
+        if (baseNode instanceof ISelectElement) {
+            return ((ISelectElement) baseNode).getOptionTextAt(index);
+        }
+        return "";
+    }
+
+    // For user annotation.
+    public Node serializeQuery(Node parent) {
+        if (baseNode == null)
+            return null;
+        return QueryService.serializeQuery(baseNode, parent);
+    }
+    
+    public char getAccessKey() {
+        if (baseNode instanceof INodeEx) {
+            return ((INodeEx) baseNode).getAccessKey();
+        }
+        return 0;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemSoundControl.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemSoundControl.java
new file mode 100644
index 0000000..dc8c3d0
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemSoundControl.java
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.impl;
+
+import org.eclipse.actf.ai.fennec.treemanager.ISoundControl;
+import org.eclipse.actf.model.dom.dombycom.AnalyzedResult;
+import org.eclipse.actf.model.dom.dombycom.INodeExSound;
+
+
+
+public class TreeItemSoundControl implements ISoundControl {
+    private AnalyzedResult analyzedResult;
+
+    public boolean muteMedia() {
+        INodeExSound[] sounds = analyzedResult.getSoundNodes();
+        boolean r = true;
+        for (int i = 0; i < sounds.length; i++) {
+            boolean st = sounds[i].getMuteState();
+            r &= sounds[i].muteMedia(!st);
+        }
+        return r;
+    }
+
+    public VolumeState getVolumeState() {
+        INodeExSound[] sounds = analyzedResult.getSoundNodes();
+        boolean mute = true;
+        boolean max = true;
+        boolean min = true;
+        for (int i = 0; i < sounds.length; i++) {
+            mute &= sounds[i].getMuteState();
+            int vol = sounds[i].getVolume();
+            max &= (vol == INodeExSound.VOLUME_MAX);
+            min &= (vol == INodeExSound.VOLUME_MIN);
+        }
+        if (mute) return VolumeState.MUTE;
+        if (max) return VolumeState.MAX;
+        if (min) return VolumeState.MIN;
+
+        return VolumeState.OTHER;
+    }
+
+    private static final int VOLUME_TICK = 150;
+    private static final int VOLUME_TICK_MINIMAL = 10;
+
+    public boolean volumeDownMedia() {
+        return volumeDownMedia(VOLUME_TICK);
+    }
+    
+    public boolean minimalVolumeDownMedia() {
+        return volumeDownMedia(VOLUME_TICK_MINIMAL);
+    }
+    
+    private boolean volumeDownMedia(int tick) {
+        INodeExSound[] sounds = analyzedResult.getSoundNodes();
+        boolean r = true;
+        for (int i = 0; i < sounds.length; i++) {
+            int vol = sounds[i].getVolume();
+            if (vol < 0) {
+                return false;
+            } else if (vol < (INodeExSound.VOLUME_MIN + tick)) {
+                vol = INodeExSound.VOLUME_MIN;
+            } else {
+                vol -= tick;
+            }
+            r &= sounds[i].setVolume(vol);
+        }
+        return r;
+    }
+
+    public boolean volumeUpMedia() {
+        return volumeUpMedia(VOLUME_TICK);
+    }        
+    
+    public boolean minimalVolumeUpMedia() {
+        return volumeUpMedia(VOLUME_TICK_MINIMAL);
+    }
+
+    private boolean volumeUpMedia(int tick) {
+        INodeExSound[] sounds = analyzedResult.getSoundNodes();
+        boolean r = true;
+        for (int i = 0; i < sounds.length; i++) {
+            int vol = sounds[i].getVolume();
+            if (vol < 0) {
+                return false;
+            } else if (vol > (INodeExSound.VOLUME_MAX - tick)) {
+                vol = INodeExSound.VOLUME_MAX;
+            } else {
+                vol += tick;
+            }
+            r &= sounds[i].setVolume(vol);
+        }
+        return r;
+    }
+
+    public int getCount() {
+        INodeExSound[] sounds = analyzedResult.getSoundNodes();
+        return sounds.length;
+    }
+
+    public static ISoundControl newTreeItemSoundControl(AnalyzedResult ar) {
+        return new TreeItemSoundControl(ar);
+    }
+
+    private TreeItemSoundControl(AnalyzedResult ar) {
+        this.analyzedResult = ar;
+    }
+
+    public int[] getVolumes(){
+        INodeExSound[] sounds = analyzedResult.getSoundNodes();
+        boolean r = true;
+        int[] volumes = new int[sounds.length];
+        for (int i = 0; i < sounds.length; i++) {
+            volumes[i] = sounds[i].getVolume();
+            
+            if (volumes[i] < 0) {
+                volumes[i] = 0;
+            }
+        }
+        return volumes;
+    }
+
+    public boolean setVolumes(int[] volumes) {
+        INodeExSound[] sounds = analyzedResult.getSoundNodes();
+        boolean r = true;
+        for (int i = 0; i < sounds.length; i++) {
+            r &= sounds[i].setVolume(volumes[i]);
+        }
+        return r;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemTerms.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemTerms.java
new file mode 100644
index 0000000..d16d1b6
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemTerms.java
@@ -0,0 +1,425 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.impl;
+
+import java.util.ArrayList;
+
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.model.dom.dombycom.INodeEx;
+import org.eclipse.actf.util.vocab.DelegationTerms;
+import org.eclipse.actf.util.vocab.IEvalTarget;
+import org.eclipse.actf.util.vocab.Vocabulary;
+import org.eclipse.swt.graphics.Rectangle;
+import org.w3c.dom.Node;
+
+
+
+
+public class TreeItemTerms extends DelegationTerms {
+
+    public TreeItemTerms(IEvalTarget delegationTarget) {
+        super(delegationTarget);
+    }
+
+    private boolean isSymbol(char c) {
+        int type = Character.getType(c);
+        if (type == Character.OTHER_SYMBOL || type == Character.MODIFIER_SYMBOL || type == Character.MATH_SYMBOL)
+            return true;
+        return false;
+    }
+
+    private boolean isSeparator(char c) {
+        switch (Character.getType(c)) {
+        case Character.SPACE_SEPARATOR:
+        case Character.LINE_SEPARATOR:
+        case Character.PARAGRAPH_SEPARATOR:
+        case Character.FORMAT:
+        case Character.CONTROL:
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isPunctuation(char c) {
+        int type = Character.getType(c);
+        if (type == Character.CONNECTOR_PUNCTUATION || type == Character.DASH_PUNCTUATION
+                || type == Character.START_PUNCTUATION || type == Character.END_PUNCTUATION
+                || type == Character.INITIAL_QUOTE_PUNCTUATION || type == Character.FINAL_QUOTE_PUNCTUATION
+                || type == Character.OTHER_PUNCTUATION)
+            return true;
+        return false;
+    }
+
+    private enum ContentCheckResult {
+        TRUE, FALSE, UNKNOWN
+    }
+    private ContentCheckResult contentCommonCheck(ITreeItem item) {
+        Object baseNode = item.getBaseNode();
+        if (baseNode instanceof IEvalTarget) {
+            if (Vocabulary.hasContent().eval((IEvalTarget) baseNode)) {
+                return ContentCheckResult.TRUE;
+            }
+        }
+
+        if (!isVisibleNode(item))
+            return ContentCheckResult.FALSE;
+        if (Vocabulary.isSelectOption().eval(item))
+            return ContentCheckResult.FALSE;
+
+        return ContentCheckResult.UNKNOWN;
+    }
+
+    @Override
+    public boolean hasContent(IEvalTarget target) {
+        if (!(target instanceof ITreeItem))
+            return false;
+        ITreeItem item = (ITreeItem) target;
+        switch (contentCommonCheck(item)) {
+        case TRUE:
+            return true;
+        case FALSE:
+            return false;
+        }
+
+        String str = item.getUIString();
+        if (str.length() == 0)
+            return false;
+        return true;
+    }
+
+    @Override
+    public boolean hasReadingContent(IEvalTarget target) {
+        if (!(target instanceof ITreeItem))
+            return false;
+        ITreeItem item = (ITreeItem) target;
+        switch (contentCommonCheck(item)) {
+        case TRUE:
+            return true;
+        case FALSE:
+            return false;
+        }
+
+        String str = item.getUIString();
+        // remove only punctuation
+        if (str.length() == 0) return false;
+
+        if (Vocabulary.isLink().eval(item))
+            return true;
+        for (int i = 0; i < str.length(); i++) {
+            if (isSeparator(str.charAt(i)) || isPunctuation(str.charAt(i)) //
+                    || isSymbol(str.charAt(i)))
+                continue;
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean isVisibleNode(IEvalTarget target) {
+        return true;
+    }
+
+    @Override
+    public boolean isBlockJumpPointF(IEvalTarget node) {
+        return isBlockJumpPoint(false, node);
+    }
+
+    @Override
+    public boolean isBlockJumpPointB(IEvalTarget node) {
+        return isBlockJumpPoint(true, node);
+    }
+
+    public boolean isBlockJumpPoint(boolean back, IEvalTarget node) {
+        if (!(node instanceof TreeItemNVM3))
+            return false;
+        TreeItemNVM3 item = (TreeItemNVM3) node;
+
+        TreeItemNVM3 prev;
+        if (!back) {
+            if (item.getNth() > 0) {
+                prev = (TreeItemNVM3) item.getParent().getChildItems()[item.getNth() - 1];
+                while (prev.hasChild()) {
+                    ITreeItem[] items = prev.getChildItems();
+                    prev = (TreeItemNVM3) items[items.length - 1];
+                }
+            } else {
+                prev = (TreeItemNVM3) item.getParent();
+            }
+        } else {
+            if (item.getNth() == item.getParent().getChildItems().length - 1) {
+                ITreeItem parent = item.getParent();
+                if (parent == null)
+                    return false;
+                while (parent.getNth() == parent.getParent().getChildItems().length - 1) {
+                    parent = parent.getParent();
+                    if (parent == null)
+                        return false;
+                }
+                prev = (TreeItemNVM3) parent.getParent().getChildItems()[parent.getNth() + 1];
+            } else {
+                if (item.hasChild()) {
+                    prev = (TreeItemNVM3) item.getChildItems()[0];
+                } else {
+                    prev = (TreeItemNVM3) item.getParent().getChildItems()[item.getNth() + 1];
+                }
+            }
+        }
+        if (prev == null)
+            return false;
+
+        item.distance = prev.distance + 1;
+        prev.distance = 0;
+
+        if (!(item.getBaseNode() instanceof INodeEx))
+            return false;
+        if (!(prev.getBaseNode() instanceof INodeEx))
+            return false;
+
+        INodeEx nex = (INodeEx) item.getBaseNode();
+        INodeEx nex2 = (INodeEx) prev.getBaseNode();
+        if (nex == null || nex2 == null)
+            return false;
+
+        Rectangle r = nex.getLocation();
+        Rectangle r2 = nex2.getLocation();
+        if (r == null || r2 == null)
+            return false;
+
+        int dist = distance(r, r2);
+        item.distance += dist;
+
+        if (r.width < 20)
+            return false;
+        if (!Vocabulary.hasContent().eval(node))
+            return false;
+        if (!Vocabulary.hasReadingContent().eval(node))
+            return false;
+        if (Vocabulary.isClickable().eval(node))
+            return false;
+        if (item.getNth() > 2 && Vocabulary.isConnectable().eval(node))
+            return false;
+
+        if (item.distance > 800) {
+            item.distance = 0;
+            return true;
+        }
+
+        if (!back && !super.isBlockJumpPointF(node))
+            return false;
+        if (back && !super.isBlockJumpPointB(node))
+            return false;
+
+        if (item.distance > 200) {
+            item.distance = 0;
+            return true;
+        }
+        return false;
+    }
+
+    private int distance(Rectangle r, Rectangle r2) {
+        return (int) Math.sqrt(Math.abs(r.x - r2.x) * Math.abs(r.y - r2.y)) //
+                + Math.abs(r.x - r2.x) + Math.abs(r.y - r2.y);
+    }
+
+    @Override
+    public boolean isHeading(int level, IEvalTarget node) {
+        if (!(node instanceof ITreeItem))
+            return false;
+        ITreeItem item = (ITreeItem) node;
+        if (level == 0)
+            return (item.getHeadingLevel() > 0);
+        else
+            return item.getHeadingLevel() == level;
+    }
+
+    @Override
+    public boolean isHeadingJumpPoint(IEvalTarget node) {
+        if (!(node instanceof ITreeItem))
+            return false;
+        ITreeItem item = (ITreeItem) node;
+
+        ITreeItem current = item;
+        short r = 0;
+        do {
+            if (current == null)
+                return super.isHeadingJumpPoint(node);
+            NVM3Metadata meta = ((TreeItemNVM3) current).getMetadata();
+            if (meta instanceof NVM3GeneratedMetadata) {
+                r = ((NVM3GeneratedMetadata) meta).getHeadingLevelByMetadata(item);
+                if (r > 0)
+                    return true;
+                else if (r == -1)
+                    return false;
+                current = current.getParent();
+            } else
+                break;
+        } while (r == 0);
+
+        if (((TreeItemNVM3) item).getMetadata() == null)
+            return false;
+        return ((TreeItemNVM3) item).getMetadata().getHeadingLevel(item) > 0;
+    }
+
+    @Override
+    public boolean isConnectable(IEvalTarget node) {
+        if (!(node instanceof TreeItemNVM3))
+            return false;
+        TreeItemNVM3 item = (TreeItemNVM3) node;
+
+        if (item.hasChild())
+            return false;
+
+        int nth = item.getNth();
+        int nextNth = nth + 1;
+
+        ITreeItem parent = ((TreeItemNVM3) node).getParent();
+        if (parent == null)
+            return false;
+
+        ITreeItem[] items = parent.getChildItems();
+        if (nextNth >= items.length)
+            return false;
+        
+        Object o = items[nextNth].getBaseNode();
+        if (o == null || !(o instanceof Node))
+            return false;
+        Node n = (Node) o;
+            
+        return Vocabulary.isReachable(n).eval(node);
+    }
+
+    @Override
+    public boolean find(String str, boolean exact, IEvalTarget node) {
+        if (!(node instanceof ITreeItem))
+            return false;
+
+        ITreeItem item = (ITreeItem) node;
+        String uiString = item.getUIString();
+
+        if (!exact) {
+            uiString = uiString.toLowerCase();
+            str = str.toLowerCase();
+        }
+
+        if (uiString.indexOf(str) != -1)
+            return true;
+
+        /*
+         for (int len = str.length() - 1; len > 0; len--) {
+         if (uiString.lastIndexOf(str.substring(0, len)) == uiString.length() - len) {
+         if (hasChild()) {
+         if(Vocabulary.startsWith(str.substring(len), exact).eval(getChildItems()[0])){
+         return true;
+         }
+         continue;
+         }
+
+         int nth = item.getNth();
+         ITreeItem parent = item.getParent();
+         while (parent != null) {
+         ITreeItem[] items = parent.getChildItems();
+         if (nth + 1 < items.length) {
+         if(Vocabulary.startsWith(str.substring(len), exact).eval(items[nth + 1])){
+         return true;
+         }
+         break;
+         }
+         nth = parent.getNth();
+         parent = parent.getParent();
+         }
+         }
+         }*/
+        return false;
+    }
+
+    @Override
+    public boolean startsWith(String str, boolean exact, IEvalTarget node) {
+        if (!(node instanceof TreeItemNVM3))
+            return false;
+        TreeItemNVM3 item = (TreeItemNVM3) node;
+        String uiString = item.getUIString();
+
+        if (!exact) {
+            uiString = uiString.toLowerCase();
+            str = str.toLowerCase();
+        }
+
+        if (uiString.length() < str.length()) {
+            if (str.startsWith(uiString)) {
+                int len = uiString.length();
+                if (item.hasChild()) {
+                    return Vocabulary.startsWith(str.substring(len), exact).eval(item.getChildItems()[0]);
+                }
+
+                int nth = item.getNth();
+                ITreeItem parent = item.getParent();
+                while (parent != null) {
+                    ITreeItem[] items = parent.getChildItems();
+                    if (nth + 1 < items.length) {
+                        return Vocabulary.startsWith(str.substring(len), exact).eval(items[nth + 1]);
+                    }
+                    nth = parent.getNth();
+                    parent = parent.getParent();
+                }
+            }
+        }
+
+        return uiString.startsWith(str);
+    }
+
+    @Override
+    public boolean nodeLocation(Node refNode, boolean backward, IEvalTarget node) {
+        if (!Vocabulary.hasContent().eval(node))
+            return false;
+        if (!(node instanceof TreeItemNVM3))
+            return false;
+        TreeItemNVM3 item = (TreeItemNVM3) node;
+
+        Node targetNode = item.getNearestNode();
+        if (targetNode == null) return false;
+
+        ArrayList<Node> refAncestors = getAncestors(refNode);
+        ArrayList<Node> ancestors = getAncestors(targetNode);
+        int i = refAncestors.size() - 1;
+        int j = ancestors.size() - 1;
+        while ((i >= 0) && (j >= 0)) {
+            Node refAncestor = refAncestors.get(i);
+            Node ancestor = ancestors.get(j);
+            if (!(ancestor.isSameNode(refAncestor))) {
+                if (!((ancestor instanceof INodeEx)) && (refAncestor instanceof INodeEx))
+                    return false;
+
+                int ancestorNth = ((INodeEx) ancestor).getNth();
+                int refAncestorNth = ((INodeEx) refAncestor).getNth();
+                if (ancestorNth == refAncestorNth)
+                    return true;
+                if (backward) {
+                    return (ancestorNth < refAncestorNth);
+                } else {
+                    return (ancestorNth > refAncestorNth);
+                }
+            }
+            i--;
+            j--;
+        }
+        return true;
+    }
+
+    private ArrayList<Node> getAncestors(Node n) {
+        ArrayList<Node> list = new ArrayList<Node>();
+        while (n != null) {
+            list.add(n);
+            n = n.getParentNode();
+        }
+        return list;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemVideoControl.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemVideoControl.java
new file mode 100644
index 0000000..5c67af6
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/impl/TreeItemVideoControl.java
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.impl;
+
+import java.util.ArrayList;
+
+import org.eclipse.actf.ai.fennec.INVM3Service;
+import org.eclipse.actf.ai.fennec.treemanager.IMediaSyncEventListener;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.fennec.treemanager.IVideoControl;
+import org.eclipse.actf.model.dom.dombycom.AnalyzedResult;
+import org.eclipse.actf.model.dom.dombycom.INodeEx;
+import org.eclipse.actf.model.dom.dombycom.INodeExVideo;
+import org.eclipse.actf.util.timer.WeakSyncTimer;
+import org.eclipse.actf.util.vocab.IProposition;
+import org.eclipse.actf.util.vocab.Vocabulary;
+
+
+
+public class TreeItemVideoControl implements IVideoControl {
+    private final INVM3Service nvm3Service;
+    private AnalyzedResult analyzedResult;
+
+    private INodeExVideo getCurrentNodeExVideo() {
+        INodeExVideo[] videos = analyzedResult.getVideoNodes();
+        if (videos.length == 0) return null;
+        if (videos.length == 1) return videos[0];
+
+        ITreeItem item = nvm3Service.getLastTreeItem();
+        for (int i = 0; i < videos.length; i++) {
+            INodeEx node = videos[i].getReferenceNode();
+            IProposition prop = Vocabulary.nodeLocation(node, true);
+            if (prop.eval(item)) return videos[i];
+        }
+        return videos[0];
+    }
+
+    public boolean previousTrack() {
+        INodeExVideo[] videos = analyzedResult.getVideoNodes();
+        boolean r = true;
+        for (int i = 0; i < videos.length; i++) {
+            r &= videos[i].previousTrack();
+        }
+        return r;
+    }
+
+    public boolean nextTrack() {
+        INodeExVideo[] videos = analyzedResult.getVideoNodes();
+        boolean r = true;
+        for (int i = 0; i < videos.length; i++) {
+            r &= videos[i].nextTrack();
+        }
+        return r;
+    }
+
+    public boolean stopMedia() {
+        INodeExVideo[] videos = analyzedResult.getVideoNodes();
+        boolean r = true;
+        for (int i = 0; i < videos.length; i++) {
+            r &= videos[i].stopMedia();
+        }
+        return r;
+    }
+
+    public boolean playMedia() {
+        INodeExVideo v = getCurrentNodeExVideo();
+
+        // Before playing the media, pause the other media to avoid audio interference.
+        INodeExVideo[] videos = analyzedResult.getVideoNodes();
+        for (int i = 0; i < videos.length; i++) {
+            if (videos[i] != v) {
+                videos[i].pauseMedia();
+            }
+        }
+        if (v != null) {
+            return v.playMedia();
+        } else {
+            return false;
+        }
+    }
+
+    public boolean pauseMedia() {
+        INodeExVideo[] videos = analyzedResult.getVideoNodes();
+        boolean pause = false;
+        for (int i = 0; i < videos.length; i++) {
+            INodeExVideo.VideoState st = videos[i].getCurrentState();
+            if ((st == INodeExVideo.VideoState.STATE_PLAY)
+                || (st == INodeExVideo.VideoState.STATE_UNKNOWN)) {
+                pause = true;
+                break;
+            }
+        }
+
+        if (pause) {
+            boolean r = true;
+            for (int i = 0; i < videos.length; i++) {
+                r &= videos[i].pauseMedia();
+            }
+            return r;
+        } else {
+            INodeExVideo v = getCurrentNodeExVideo();
+            if (v != null) {
+                return v.playMedia();
+            } else {
+                return false;
+            }
+        }
+    }
+
+    public IVideoControl.VideoState getVideoState() {
+        INodeExVideo[] videos = analyzedResult.getVideoNodes();
+        IVideoControl.VideoState st = IVideoControl.VideoState.STATE_OTHER;
+        for (int i = 0; i < videos.length; i++) {
+            INodeExVideo.VideoState stn = videos[i].getCurrentState();
+            switch (stn) {
+            case STATE_UNKNOWN:
+                break;
+            case STATE_PLAY:
+                st = IVideoControl.VideoState.STATE_PLAY;
+                break;
+            case STATE_STOP:
+                if (st == IVideoControl.VideoState.STATE_OTHER) {
+                    st = IVideoControl.VideoState.STATE_STOP;
+                }
+                break;
+            case STATE_PAUSE:
+                if (st == IVideoControl.VideoState.STATE_OTHER) {
+                    st = IVideoControl.VideoState.STATE_PAUSE;
+                }
+                break;
+            case STATE_WAITING:
+                if (st == IVideoControl.VideoState.STATE_OTHER) {
+                    st = IVideoControl.VideoState.STATE_WAITING;
+                }
+                break;
+            case STATE_FASTFORWARD:
+                if (st != IVideoControl.VideoState.STATE_PLAY) {
+                    st = IVideoControl.VideoState.STATE_FASTFORWARD;
+                }
+                break;
+            case STATE_FASTREVERSE:
+                if (st != IVideoControl.VideoState.STATE_PLAY) {
+                    st = IVideoControl.VideoState.STATE_FASTREVERSE;
+                }
+            }
+        }
+        return st;
+    }
+
+    public boolean fastReverse() {
+        INodeExVideo v = getCurrentNodeExVideo();
+        if (v != null) {
+            return v.fastReverse();
+        } else {
+            return false;
+        }
+    }
+
+    public boolean fastForward() {
+        INodeExVideo v = getCurrentNodeExVideo();
+        if (v != null) {
+            return v.fastForward();
+        } else {
+            return false;
+        }
+    }
+
+    public double getCurrentPosition() {
+        INodeExVideo v = getCurrentNodeExVideo();
+        if (v != null) {
+            return v.getCurrentPosition();
+        } else {
+            return 0;
+        }
+    }
+
+    public double getTotalLength() {
+        INodeExVideo v = getCurrentNodeExVideo();
+        if (v != null) {
+            return v.getTotalLength();
+        } else {
+            return 0;
+        }
+    }
+
+    public int getCount() {
+        INodeExVideo[] videos = analyzedResult.getVideoNodes();
+        return videos.length;
+    }
+
+    public int getIndex() {
+        INodeExVideo[] videos = analyzedResult.getVideoNodes();
+        if (videos.length == 0) return -1;
+        if (videos.length == 1) return 0;
+
+        ITreeItem item = nvm3Service.getLastTreeItem();
+        for (int i = 0; i < videos.length; i++) {
+            INodeEx node = videos[i].getReferenceNode();
+            IProposition prop = Vocabulary.nodeLocation(node, true);
+            if (prop.eval(item)) return i;
+        }
+        return 0;
+    }
+
+
+    public static IVideoControl newTreeItemVideoControl(AnalyzedResult ar,
+                                                        INVM3Service nvm3Service) {
+        return new TreeItemVideoControl(ar, nvm3Service);
+    }
+
+    private TreeItemVideoControl(AnalyzedResult ar,
+                                 INVM3Service nvm3Service) {
+        this.analyzedResult = ar;
+        this.nvm3Service = nvm3Service;
+    }
+
+    // This ArrayList ensures strong references of currently valid listeners.
+    private ArrayList<IMediaSyncEventListener> listeners;
+    public boolean addEventListener(IMediaSyncEventListener listener) {
+        WeakSyncTimer timer = WeakSyncTimer.getTimer();
+        timer.addEventListener(listener);
+        if (listeners == null) {
+            listeners = new ArrayList<IMediaSyncEventListener>(1);
+        }
+        listeners.add(listener);
+        
+        return true;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/mediator/NVM3MediatorImpl.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/mediator/NVM3MediatorImpl.java
new file mode 100644
index 0000000..04f7a62
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/mediator/NVM3MediatorImpl.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.mediator;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.actf.ai.fennec.INVM3Entry;
+import org.eclipse.actf.ai.fennec.INVM3Mediator;
+import org.eclipse.actf.ai.fennec.INVM3Service;
+import org.eclipse.actf.ai.fennec.NVM3ServiceFactory;
+import org.eclipse.actf.ai.fennec.impl.NVM3DOMReader;
+import org.eclipse.actf.ai.fennec.impl.NVM3EntryImpl;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeManager;
+import org.eclipse.actf.ai.fennec.treemanager.TreeManagerFactory;
+import org.eclipse.actf.ai.xmlstore.IXMLInfo;
+import org.eclipse.actf.ai.xmlstore.IXMLSelector;
+import org.eclipse.actf.ai.xmlstore.IXMLStore;
+import org.eclipse.actf.ai.xmlstore.IXMLStoreService;
+import org.eclipse.actf.ai.xmlstore.XMLStorePlugin;
+import org.eclipse.actf.model.IWebBrowserACTF;
+import org.eclipse.actf.model.dom.dombycom.IDocumentEx;
+
+
+
+
+public class NVM3MediatorImpl implements INVM3Mediator {
+    private final IWebBrowserACTF webBrowser;
+
+    public ITreeManager newTreeManager(INVM3Entry entry) {
+        IDocumentEx doc = (IDocumentEx) webBrowser.getLiveDocument();
+
+        INVM3Service nvm3Service;
+        if (entry != null) {
+            try {
+                nvm3Service = NVM3ServiceFactory.newNVM3Service(entry, doc);
+            } catch (Exception e) {
+                nvm3Service = NVM3ServiceFactory.newNVM3ServiceWithDefaultMetadata(doc);
+            }
+        } else {
+            nvm3Service = NVM3ServiceFactory.newNVM3ServiceWithDefaultMetadata(doc);
+        }
+        return TreeManagerFactory.newITreeManager(nvm3Service);
+    }
+
+    private IXMLStore getNVM3Store(String url) {
+        IXMLStoreService ss = XMLStorePlugin.getDefault().getXMLStoreService();
+        IXMLSelector selector = ss.getSelectorWithDocElem(NVM3DOMReader.NVM3_DOCUMENT_ELEMENT_NAME,
+                                                          NVM3DOMReader.NVM3_NAMESPACE_URI);
+        IXMLStore store = ss.getRootStore();
+        store = store.specify(selector);
+        if (store == null) return null;
+        selector = ss.getSelectorWithIRI(url);
+        return store.specify(selector);
+    }
+
+    public INVM3Entry getDefaultNVM3Entry() {
+        String url = webBrowser.getURL();
+        IXMLStore store = getNVM3Store(url);
+        if (store == null) return null;
+        Iterator<IXMLInfo> it = store.getInfoIterator();
+        if (it == null) return null;
+        if (!it.hasNext()) return null;
+        IXMLInfo info = it.next();
+        return new NVM3EntryImpl(info);
+    }
+
+    public INVM3Entry[] getNVM3Entries() {
+        String url = webBrowser.getURL();
+        IXMLStore store = getNVM3Store(url);
+        if (store == null) return null;
+        Iterator<IXMLInfo> it = store.getInfoIterator();
+        if (it == null) return null;
+        ArrayList<NVM3EntryImpl> entries = new ArrayList<NVM3EntryImpl>();
+        while (it.hasNext()) {
+            IXMLInfo info = it.next();
+            if (info == null) continue;
+            entries.add(new NVM3EntryImpl(info));
+        }
+        INVM3Entry[] ea = new INVM3Entry[entries.size()];
+        ea = entries.toArray(ea);
+        return ea;
+    }
+
+    public void release() {
+        // this.dombycom.release();
+    }
+
+    public NVM3MediatorImpl(IWebBrowserACTF webBrowser) {
+        this.webBrowser = webBrowser;
+    }
+}
+
+
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/IAccessKeyList.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/IAccessKeyList.java
new file mode 100644
index 0000000..3c98741
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/IAccessKeyList.java
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.treemanager;
+
+
+public interface IAccessKeyList {
+    int size();
+    char getAccessKeyAt(int index);
+    String getUIStringAt(int index);
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ILocation.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ILocation.java
new file mode 100644
index 0000000..44e7781
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ILocation.java
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.treemanager;
+
+
+public interface ILocation {
+
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/IMediaSyncEventListener.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/IMediaSyncEventListener.java
new file mode 100644
index 0000000..7563bf3
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/IMediaSyncEventListener.java
@@ -0,0 +1,19 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.treemanager;
+
+import org.eclipse.actf.util.timer.SyncEventListener;
+
+
+
+public interface IMediaSyncEventListener extends SyncEventListener {
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ISoundControl.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ISoundControl.java
new file mode 100644
index 0000000..78b6471
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ISoundControl.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.treemanager;
+
+
+public interface ISoundControl {
+    public enum VolumeState {
+        MIN,
+        MAX,
+        OTHER,
+        MUTE
+    }
+
+    boolean muteMedia();
+
+    VolumeState getVolumeState();
+
+    boolean volumeDownMedia();
+
+    boolean volumeUpMedia();
+    
+    boolean minimalVolumeDownMedia();
+    
+    boolean minimalVolumeUpMedia();
+
+    int getCount();
+    
+    int[] getVolumes();
+    
+    boolean setVolumes(int[] volumes);
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITable.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITable.java
new file mode 100644
index 0000000..6c22468
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITable.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.treemanager;
+
+
+
+public interface ITable {
+    ITableRow[] getRow(int idx);
+    int getColumnSize();
+    int getRowSize();
+    String getTableString();
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITableCell.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITableCell.java
new file mode 100644
index 0000000..96ebac5
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITableCell.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.treemanager;
+
+
+
+public interface ITableCell {
+    int getColumn();
+    int getRow();
+    ITreeItem getItem();
+
+    boolean isConnectedWithUpCell();
+    boolean isConnectedWithLeftCell();
+
+    ITreeItem getRowHeader();
+    ITreeItem getColumnHeader();
+
+    boolean isHeader();
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITableRow.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITableRow.java
new file mode 100644
index 0000000..10d2c33
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITableRow.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.treemanager;
+
+
+
+public interface ITableRow {
+    ITableCell[] getTableCell();
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITreeItem.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITreeItem.java
new file mode 100644
index 0000000..a833b35
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITreeItem.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.treemanager;
+
+import org.eclipse.actf.util.vocab.AbstractTerms;
+import org.eclipse.actf.util.vocab.IEvalTarget;
+import org.w3c.dom.Node;
+
+
+
+/**
+ * The main plugin class to be used in the desktop.
+ */
+public interface ITreeItem extends IEvalTarget {
+    // Get the parent item.  If this is the root, return null.
+    ITreeItem getParent();
+
+    // Get the child items.
+    // Even if it has no children, return an empty array (size == 0) instead of null.
+    ITreeItem[] getChildItems();
+
+    // return nth of the children of this parent.
+    int getNth();
+
+    // short text.
+    String getUIString();
+
+    // long descriptive text.
+    String getDescription();
+    
+    // Return a text to represent the node.
+    // This text is inappropriate to be notified to users.
+    String getNodeString();
+
+    // Heading level.  0 means this node is not a heading.
+    short getHeadingLevel();
+
+    // Return the URI of the link of this tree item if it has a link.
+    String getLinkURI();
+
+    // This is a deprecated API.  It may breaks independence on content types.
+    Object getBaseNode();
+
+    AbstractTerms getTerms();
+    
+    // Return true if this item accepts some input.
+    // !FN!
+    boolean isInputable();
+
+    // Return true if this item accepts click operations.
+    // !FN!
+    boolean isClickable();
+
+    // Return true if this item contains some image.
+    // !FN!
+    boolean isImage();
+
+    // [0]...mimetype [1]...URI [2] ... png clut URI
+    // !FN!
+    String[] getStillPictureData();
+
+    // Return the URI of the image.
+    // String getImageURI();
+
+    // Click this item.
+    int doClick() throws TreeManagerException;
+
+    // Optional API.  Do not use it currently.
+    int stay() throws TreeManagerException;
+
+    // Highlight this item.
+    int highlight() throws TreeManagerException;
+
+    // Unhighlight this item.
+    int unhighlight() throws TreeManagerException;
+    
+    // Set focus to this item.
+    // This method only sets the input focus if possible.
+    boolean setFocus();
+
+    // Set the text to this item.  (This method may be used for a text edit widget)
+    int setText(String text) throws TreeManagerException;
+
+    // Return the set text.
+    String getText() throws TreeManagerException;
+
+    // Form
+    int getRadioIndex();
+
+    int getRadioTotal();
+
+    void setSelectedIndices(int[] indices);
+
+    int[] getSelectedIndices();
+
+    int getOptionsCount();
+
+    String getOptionTextAt(int i);
+
+    int getListIndex();
+    
+    int getListTotal();
+    
+    public String getFormLabel();
+
+    // User Annotation.
+    Node serializeQuery(Node parent);
+
+    char getAccessKey();
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITreeManager.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITreeManager.java
new file mode 100644
index 0000000..79a49b6
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/ITreeManager.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.treemanager;
+
+import org.eclipse.actf.util.vocab.IProposition;
+
+
+public interface ITreeManager {
+    // Status code.
+    int NOACTION = 0;
+    int MOVED = 1 << 0;
+    int LEVEL_CHANGED = 1 << 1;
+    int TRANSFERRED = 1 << 2;
+    int CLICKED = 1 << 3;
+    int CHANGED = 1 << 4;
+    int PARENT_CHANGED = 1 << 5;
+    int FOUND = 1 << 6;
+    int UNDONE = 1 << 8;
+    int ERROR = 1 << 16;
+
+    // Status API
+    int getLevel() throws TreeManagerException;
+
+    // Returns the active Item.
+    ITreeItem getActiveItem() throws TreeManagerException;
+
+    // Used for media control.  It will be subject to change.
+    ISoundControl getSoundControl() throws TreeManagerException;
+
+    // Used for media control.  It will be subject to change.
+    IVideoControl getVideoControl() throws TreeManagerException;
+    
+    // Used for accesskey
+    IAccessKeyList getAccessKeyList() throws TreeManagerException;
+
+    // Returns the siblings of the active item.
+    ITreeItem[] getSiblings() throws TreeManagerException;
+
+    // initialize. API.
+    int initialize() throws TreeManagerException;
+
+    // Action APIs
+
+    // Tentative API.  Do not use it currently.
+    int stay() throws TreeManagerException;
+
+    // 
+    int click(boolean doClick) throws TreeManagerException;
+
+    // Tree Navigation API
+    int gotoParent() throws TreeManagerException;
+
+    int gotoFirstChild() throws TreeManagerException;
+
+    int gotoStartOfSiblings() throws TreeManagerException;
+
+    int gotoEndOfSiblings() throws TreeManagerException;
+
+    int gotoPreviousSibling() throws TreeManagerException;
+
+    int gotoNextSibling() throws TreeManagerException;
+
+    int gotoStartOfPage() throws TreeManagerException;
+
+    int gotoEndOfPage() throws TreeManagerException;
+
+    int traverse(boolean back) throws TreeManagerException;
+
+    // Search functions.  (Extension)
+
+    int gotoPreviousLine() throws TreeManagerException;
+
+    int gotoNextLine() throws TreeManagerException;
+
+    int gotoStartOfLine() throws TreeManagerException;
+
+    int gotoEndOfLine() throws TreeManagerException;
+
+    int findNext(IProposition proposition) throws TreeManagerException;
+
+    int findPrevious(IProposition proposition) throws TreeManagerException;
+
+    int skipToAnchor(String target) throws TreeManagerException;
+
+    // Table Navigation API.
+
+    int gotoLeftCell() throws TreeManagerException;
+
+    int gotoRightCell() throws TreeManagerException;
+
+    int gotoUpCell() throws TreeManagerException;
+
+    int gotoDownCell() throws TreeManagerException;
+
+    int getActiveTableInfo() throws TreeManagerException;
+
+    int getActiveTableCellInfo() throws TreeManagerException;
+
+    // Optional API.  
+    ITreeItem getCurrentRootItem() throws TreeManagerException;
+
+    // Experimental API.
+    int analyze() throws TreeManagerException;
+    
+    ILocation getCurrentLocation() throws TreeManagerException;
+    
+    int moveToLocation(ILocation location) throws TreeManagerException;
+
+    // 
+    ITreeItem expandWholeTree() throws TreeManagerException;
+
+    int gotoEndOfPageForFind() throws TreeManagerException;
+
+    void repairFlash() throws TreeManagerException;
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/IVideoControl.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/IVideoControl.java
new file mode 100644
index 0000000..436c0a9
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/IVideoControl.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.treemanager;
+
+
+public interface IVideoControl {
+    boolean addEventListener(IMediaSyncEventListener listener);
+
+    public enum VideoState {
+        STATE_OTHER,
+        STATE_PLAY,
+        STATE_STOP,
+        STATE_PAUSE,
+        STATE_FASTFORWARD,
+        STATE_FASTREVERSE,
+        STATE_WAITING
+    }
+
+    VideoState getVideoState();
+    
+    boolean previousTrack();
+
+    boolean nextTrack();
+
+    boolean stopMedia();
+
+    boolean playMedia();
+
+    boolean pauseMedia();
+
+    boolean fastReverse();
+
+    boolean fastForward();
+
+    double getCurrentPosition();
+
+    double getTotalLength();
+
+    int getCount();
+    
+    int getIndex();
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/TreeManagerException.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/TreeManagerException.java
new file mode 100644
index 0000000..edb6452
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/TreeManagerException.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.treemanager;
+
+public class TreeManagerException extends Exception {
+    private static final long serialVersionUID = -715217633494025285L;
+    
+    private final int status;
+
+    public int getStatus() {
+        return status;
+    }
+    
+    public TreeManagerException(int status, String message, Throwable cause) {
+        super(message, cause);
+        this.status = status;
+    }
+
+    public TreeManagerException(int status, String message) {
+        super(message);
+        this.status = status;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/TreeManagerFactory.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/TreeManagerFactory.java
new file mode 100644
index 0000000..4b9cc63
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/TreeManagerFactory.java
@@ -0,0 +1,21 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.treemanager;
+
+import org.eclipse.actf.ai.fennec.INVM3Service;
+import org.eclipse.actf.ai.fennec.treemanager.impl.TreeManagerImpl;
+
+
+public class TreeManagerFactory {
+    public static ITreeManager newITreeManager(INVM3Service nvm3Service) {
+        return new TreeManagerImpl(nvm3Service);
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/TreeManagerInterruptedException.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/TreeManagerInterruptedException.java
new file mode 100644
index 0000000..362d5ca
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/TreeManagerInterruptedException.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.treemanager;
+
+public class TreeManagerInterruptedException extends TreeManagerException {
+    private static final long serialVersionUID = -1029176053941063206L;
+
+    public TreeManagerInterruptedException(int status, String message, Throwable cause) {
+        super(status, message, cause);
+    }
+
+    public TreeManagerInterruptedException(int status, String message) {
+        super(status, message);
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/TreeManagerPlugin.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/TreeManagerPlugin.java
new file mode 100644
index 0000000..b870d11
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/TreeManagerPlugin.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.treemanager;
+
+import org.eclipse.core.runtime.Plugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The main plugin class to be used in the desktop.
+ */
+public class TreeManagerPlugin extends Plugin {
+
+	//The shared instance.
+	private static TreeManagerPlugin plugin;
+	
+	/**
+	 * The constructor.
+	 */
+	public TreeManagerPlugin() {
+		plugin = this;
+	}
+
+	/**
+	 * This method is called upon plug-in activation
+	 */
+	@Override
+    public void start(BundleContext context) throws Exception {
+		super.start(context);
+	}
+
+	/**
+	 * This method is called when the plug-in is stopped
+	 */
+	@Override
+    public void stop(BundleContext context) throws Exception {
+		super.stop(context);
+		plugin = null;
+	}
+
+	/**
+	 * Returns the shared instance.
+	 */
+	public static TreeManagerPlugin getDefault() {
+		return plugin;
+	}
+
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/impl/LocationImpl.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/impl/LocationImpl.java
new file mode 100644
index 0000000..69138fe
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/impl/LocationImpl.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.fennec.treemanager.impl;
+
+import org.eclipse.actf.ai.fennec.treemanager.ILocation;
+
+
+
+class LocationImpl implements ILocation {
+    private final int[] pos;
+    
+    int[] getPos() {
+        return pos;
+    }
+    
+    LocationImpl(int[] pos) {
+        this.pos = pos;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/impl/TreeManagerImpl.java b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/impl/TreeManagerImpl.java
new file mode 100644
index 0000000..45b74ae
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.fennec/src/org/eclipse/actf/ai/fennec/treemanager/impl/TreeManagerImpl.java
@@ -0,0 +1,604 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.fennec.treemanager.impl;
+
+import java.util.ArrayList;
+
+import org.eclipse.actf.ai.fennec.INVM3Service;
+import org.eclipse.actf.ai.fennec.NVM3Exception;
+import org.eclipse.actf.ai.fennec.NVM3InterruptedException;
+import org.eclipse.actf.ai.fennec.treemanager.IAccessKeyList;
+import org.eclipse.actf.ai.fennec.treemanager.ILocation;
+import org.eclipse.actf.ai.fennec.treemanager.ISoundControl;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeManager;
+import org.eclipse.actf.ai.fennec.treemanager.IVideoControl;
+import org.eclipse.actf.ai.fennec.treemanager.TreeManagerException;
+import org.eclipse.actf.ai.fennec.treemanager.TreeManagerInterruptedException;
+import org.eclipse.actf.model.dom.dombycom.IFlashNode;
+import org.eclipse.actf.util.vocab.IProposition;
+import org.eclipse.actf.util.vocab.Vocabulary;
+
+
+/**
+ * The main plugin class to be used in the desktop.
+ */
+public class TreeManagerImpl implements ITreeManager {
+    private INVM3Service nvm3Service;
+
+    public TreeManagerImpl(INVM3Service nvm3Service) {
+        this.nvm3Service = nvm3Service;
+    }
+
+    public int getLevel() throws TreeManagerException {
+        int i = 0;
+        ITreeItem ita = getActiveItem();
+        if (ita == null)
+            return 0;
+        while (true) {
+            ita = ita.getParent();
+            if (ita == null)
+                break;
+            i++;
+        }
+        return i;
+    }
+
+    public ITreeItem getActiveItem() throws TreeManagerException {
+        return nvm3Service.getLastTreeItem();
+    }
+
+    public ITreeItem[] getSiblings() throws TreeManagerException {
+        ITreeItem item = getActiveItem();
+        if (item == null)
+            return null;
+        ITreeItem parent = item.getParent();
+        if (parent == null) {
+            ITreeItem[] itas = new ITreeItem[1];
+            itas[0] = getActiveItem();
+            return itas;
+        }
+        return parent.getChildItems();
+    }
+
+    private void initNVM3Service() throws TreeManagerException {
+        if (nvm3Service.getStatus() == INVM3Service.NORMAL)
+            return;
+        try {
+            nvm3Service.initialize();
+        } catch (NVM3InterruptedException e) {
+            throw new TreeManagerInterruptedException(ITreeManager.UNDONE, e.getMessage(), e);
+        } catch (NVM3Exception e) {
+            throw new TreeManagerException(ITreeManager.ERROR, e.getMessage(), e);
+        }
+    }
+
+    public int initialize() throws TreeManagerException {
+        initNVM3Service();
+        return ITreeManager.NOACTION;
+    }
+
+    private int moveUpdate(ITreeItem target, boolean update) throws TreeManagerException {
+        try {
+            return nvm3Service.moveUpdate(target, update);
+        } catch (NVM3InterruptedException e) {
+            throw new TreeManagerInterruptedException(ITreeManager.UNDONE, "Failed to update by move.", e);
+        } catch (NVM3Exception e) {
+            throw new TreeManagerException(ITreeManager.ERROR, "Failed to update by move.", e);
+        }
+    }
+
+    private int clickUpdate(ITreeItem target) throws TreeManagerException {
+        try {
+            return nvm3Service.clickUpdate(target);
+        } catch (NVM3InterruptedException e) {
+            throw new TreeManagerInterruptedException(ITreeManager.UNDONE, "Failed to update by click.", e);
+        } catch (NVM3Exception e) {
+            throw new TreeManagerException(ITreeManager.ERROR, "Failed to update by click.", e);
+        }
+    }
+
+    private int gotoRoot() throws TreeManagerException {
+        ITreeItem parent = getActiveItem().getParent();
+        if (parent == null)
+            return ITreeManager.NOACTION;
+        while (gotoParent() != ITreeManager.NOACTION)
+            ;
+        return ITreeManager.MOVED | ITreeManager.LEVEL_CHANGED;
+    }
+
+    public int stay() throws TreeManagerException {
+        initNVM3Service();
+        return getActiveItem().stay();
+    }
+
+    public int click(boolean doClick) throws TreeManagerException {
+        initNVM3Service();
+        ITreeItem item = getActiveItem();
+        if (doClick)
+            item.doClick();
+        return clickUpdate(item);
+    }
+
+    public int gotoParent() throws TreeManagerException {
+        initNVM3Service();
+        ITreeItem parent = getActiveItem().getParent();
+        if (parent != null) {
+            int st = moveUpdate(parent, true);
+            if ((st & ITreeManager.MOVED) != 0)
+                st |= ITreeManager.LEVEL_CHANGED;
+            return st;
+        }
+        return ITreeManager.NOACTION;
+    }
+
+    public int gotoFirstChild() throws TreeManagerException {
+        initNVM3Service();
+        ITreeItem active = getActiveItem();
+        ITreeItem[] childItems = active.getChildItems();
+        if (childItems.length == 0)
+            return ITreeManager.NOACTION;
+        int st = moveUpdate(childItems[0], true);
+        if ((st & ITreeManager.MOVED) != 0)
+            st |= ITreeManager.LEVEL_CHANGED;
+        return st;
+    }
+
+    private int gotoLastChild() throws TreeManagerException {
+        initNVM3Service();
+        ITreeItem active = getActiveItem();
+        ITreeItem[] childItems = active.getChildItems();
+        if (childItems.length == 0)
+            return ITreeManager.NOACTION;
+        int st = moveUpdate(childItems[childItems.length - 1], true);
+        if ((st & ITreeManager.MOVED) != 0)
+            st |= ITreeManager.LEVEL_CHANGED;
+        return st;
+    }
+
+    private int gotoSiblingIndex(int idx) throws TreeManagerException {
+        initNVM3Service();
+        ITreeItem active = getActiveItem();
+        ITreeItem[] siblingItems = getSiblings();
+        if ((idx < 0) || (siblingItems.length <= idx) || (active.equals(siblingItems[idx])))
+            return ITreeManager.NOACTION;
+        int st = moveUpdate(siblingItems[idx], true);
+        return st;
+    }
+
+    private int getActiveIndex() throws TreeManagerException {
+        initNVM3Service();
+        return getActiveItem().getNth();
+    }
+
+    public int gotoStartOfSiblings() throws TreeManagerException {
+        return gotoSiblingIndex(0);
+    }
+
+    public int gotoEndOfSiblings() throws TreeManagerException {
+        initNVM3Service();
+        ITreeItem[] siblingItems = getSiblings();
+        int st = moveUpdate(siblingItems[siblingItems.length - 1], true);
+        return st | ITreeManager.MOVED;
+    }
+
+    public int gotoPreviousSibling() throws TreeManagerException {
+        int idx = getActiveIndex();
+        return gotoSiblingIndex(idx - 1);
+    }
+
+    public int gotoNextSibling() throws TreeManagerException {
+        int idx = getActiveIndex();
+        return gotoSiblingIndex(idx + 1);
+    }
+
+    public int gotoStartOfPage() throws TreeManagerException {
+        ITreeItem item = getCurrentRootItem();
+        int st;
+        st = moveUpdate(item, true);
+        return ITreeManager.NOACTION;
+
+        /*
+         ITreeItem[] childItems = item.getChildItems();
+         int st;
+         if ((childItems != null) && (childItems.length > 0)) {
+         st = moveUpdate(childItems[0], true);
+         } else {
+         st = moveUpdate(item, true);
+         }
+         if ((st & ITreeManager.MOVED) != 0)
+         st |= ITreeManager.LEVEL_CHANGED;
+         return st;*/
+    }
+
+    public int gotoEndOfPageForFind() throws TreeManagerException {
+        // TODO
+        int st;
+        this.gotoStartOfPage();
+        do {
+            st = this.gotoEndOfSiblings();
+        } while (this.gotoFirstChild() != ITreeManager.NOACTION);
+
+        if ((st & ITreeManager.MOVED) != 0)
+            st |= ITreeManager.LEVEL_CHANGED;
+        return st;
+    }
+
+    public int gotoEndOfPage() throws TreeManagerException {
+        // TODO
+        int st;
+        this.gotoStartOfPage();
+        do {
+            st = this.gotoEndOfSiblings();
+        } while (this.gotoFirstChild() != ITreeManager.NOACTION);
+
+        ITreeItem item = getActiveItem();
+        while (!Vocabulary.hasReadingContent().eval(item)) {
+            st = this.traverse(true);
+            item = getActiveItem();
+        }
+
+        if ((st & ITreeManager.MOVED) != 0)
+            st |= ITreeManager.LEVEL_CHANGED;
+        return st;
+    }
+
+    public int gotoPreviousLine() throws TreeManagerException {
+        // TODO
+        return ITreeManager.NOACTION;
+    }
+
+    public int gotoNextLine() throws TreeManagerException {
+        // TODO
+        return ITreeManager.NOACTION;
+    }
+
+    public int gotoStartOfLine() throws TreeManagerException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int gotoEndOfLine() throws TreeManagerException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    private int findItem(boolean back, IProposition proposition) throws TreeManagerException {
+        try {
+            ITreeItem item;
+            int[] pos = getActivePos();
+            int level = pos.length;
+            int rc;
+            if (back) {
+                // First, traverse to the next item in order to skip the items
+                // connectable with the current item.
+                rc = traverse(true);
+                item = getActiveItem();
+                if (proposition.eval(item)) {
+                    rc |= ITreeManager.FOUND;
+                } else {
+                    rc = nvm3Service.searchBackward(proposition);
+                }
+            } else {
+                // Likewise.
+                rc = traverse(false);
+                item = getActiveItem();
+                if (proposition.eval(item)) {
+                    rc |= ITreeManager.FOUND;
+                } else {
+                    rc = nvm3Service.searchForward(proposition);
+                }
+            }
+            if ((rc & ITreeManager.FOUND) == 0) {
+                setActivePos(pos);
+                return rc;
+            }
+            item = getActiveItem();
+            if (back) {
+                // Skip to the interval start.
+                ITreeItem siblings[] = getSiblings();
+                int idx = item.getNth();
+                int st = intervalStart(siblings, idx);
+                if (idx != st) {
+                    rc |= moveUpdate(siblings[st], false);
+                }
+                item = getActiveItem();
+            }
+            if (!Vocabulary.hasReadingContent().eval(item)) {
+                traverse(false);
+            }
+            if (level != getLevel()) {
+                rc |= ITreeManager.LEVEL_CHANGED;
+            }
+            return rc;
+        } catch (NVM3InterruptedException e) {
+            throw new TreeManagerInterruptedException(ITreeManager.UNDONE, "Failed to search.", e);
+        } catch (NVM3Exception e) {
+            throw new TreeManagerException(ITreeManager.ERROR, "Failed to search.", e);
+        }
+    }
+
+    private int[] getActivePos() throws TreeManagerException {
+        int[] ret = new int[getLevel()];
+        ITreeItem current = getActiveItem();
+        int count = ret.length;
+        while (count > 0) {
+            ret[count - 1] = current.getNth();
+            count--;
+            current = current.getParent();
+        }
+        return ret;
+    }
+
+    private int setActivePos(int[] indexes) throws TreeManagerException {
+        gotoRoot();
+        ITreeItem item;
+        int st = ITreeManager.NOACTION;
+        for (int i = 0; i < indexes.length; i++) {
+            item = getActiveItem();
+            ITreeItem[] children = item.getChildItems();
+            int idx = indexes[i];
+            if ((children == null) || (idx >= children.length))
+                break;
+            st |= moveUpdate(children[idx], true);
+        }
+        return st;
+    }
+
+    public int findNext(IProposition proposition) throws TreeManagerException {
+        return findItem(false, proposition);
+    }
+
+    public int findPrevious(IProposition proposition) throws TreeManagerException {
+        return findItem(true, proposition);
+    }
+
+    private int traverseForward(ITreeItem active) throws TreeManagerException {
+        ITreeItem[] childItems = active.getChildItems();
+        if (childItems.length != 0) {
+            int st = moveUpdate(childItems[0], false);
+            return st;
+        }
+        int idx = active.getNth();
+        ITreeItem[] siblings = getSiblings();
+        if ((idx >= 0) && ((idx + 1) < siblings.length)) {
+            int st = moveUpdate(siblings[idx + 1], false);
+            return st;
+        }
+
+        for (int upCount = 1;; upCount++) {
+            active = getActiveItem();
+            ITreeItem parent = active.getParent();
+            if (parent == null) {
+                for (; upCount > 0; upCount--) {
+                    gotoLastChild();
+                }
+                return ITreeManager.NOACTION;
+            }
+            if (moveUpdate(parent, false) == ITreeManager.NOACTION) {
+                throw new TreeManagerException(ITreeManager.ERROR, "Internal Error.");
+            }
+            idx = getActiveIndex();
+            siblings = getSiblings();
+            if ((idx >= 0) && ((idx + 1) < siblings.length)) {
+                int st = moveUpdate(siblings[idx + 1], false);
+                return st;
+            }
+        }
+    }
+
+    private int traverseBackward() throws TreeManagerException {
+        int idx = getActiveIndex();
+        if (idx <= 0) {
+            ITreeItem active = getActiveItem();
+            ITreeItem parent = active.getParent();
+            if (parent == null)
+                return ITreeManager.NOACTION;
+            int st = moveUpdate(parent, false);
+            return st;
+        }
+        ITreeItem[] siblings = getSiblings();
+        int st = moveUpdate(siblings[idx - 1], false);
+
+        for (;;) {
+            ITreeItem active = getActiveItem();
+            ITreeItem[] childItems = active.getChildItems();
+            if (childItems.length == 0)
+                break;
+            st |= moveUpdate(childItems[0], false) | ITreeManager.LEVEL_CHANGED;
+            siblings = getSiblings();
+            if (siblings.length > 1) {
+                st |= moveUpdate(siblings[siblings.length - 1], false);
+            }
+        }
+        return st;
+    }
+
+    private int intervalStart(ITreeItem[] siblings, int st) {
+        for (st = st - 1;; st--) {
+            if (st < 0)
+                return 0;
+            if (!Vocabulary.isConnectable().eval(siblings[st]))
+                return st + 1;
+        }
+    }
+
+    private int intervalEnd(ITreeItem[] siblings, int end) {
+        for (; end < siblings.length; end++) {
+            if (!Vocabulary.isConnectable().eval(siblings[end]))
+                return end;
+        }
+        return end - 1;
+    }
+
+    public int traverse(boolean back) throws TreeManagerException {
+        initNVM3Service();
+        ITreeItem orig = getActiveItem();
+        ITreeItem current = orig;
+        int level = getLevel();
+        int rc = ITreeManager.NOACTION;
+
+        if (back) {
+            do {
+                rc = traverseBackward();
+                current = getActiveItem();
+            } while ((rc != ITreeManager.NOACTION)
+                    && (!Vocabulary.hasReadingContent().eval(current)));
+            ITreeItem siblings[] = getSiblings();
+            int idx = current.getNth();
+            int st = intervalStart(siblings, idx);
+            if (idx != st) {
+                rc |= moveUpdate(siblings[st], false);
+            }
+        } else {
+            ITreeItem siblings[] = getSiblings();
+            int idx = current.getNth();
+            int end = intervalEnd(siblings, idx);
+            current = siblings[end];
+            do {
+                rc = traverseForward(current);
+                current = getActiveItem();
+            } while ((rc != ITreeManager.NOACTION)
+                    && (!Vocabulary.hasReadingContent().eval(current)));
+        }
+
+        if (!orig.equals(current)) {
+            // rc |= ITreeManager.MOVED;
+            if (level != getLevel()) {
+                rc |= ITreeManager.LEVEL_CHANGED;
+            }
+        }
+
+        return rc;
+    }
+
+    public String[] getActiveStrings() throws TreeManagerException {
+        initNVM3Service();
+        ArrayList al = new ArrayList();
+        ITreeItem[] siblings = getSiblings();
+        for (int i = getActiveIndex(); i < siblings.length; i++) {
+            ITreeItem current = siblings[i];
+            String str = current.getUIString();
+            if (str.length() > 0) {
+                al.add(str);
+            }
+            ITreeItem[] childItems = current.getChildItems();
+            if (childItems.length > 0)
+                break;
+        }
+        return (String[]) al.toArray(new String[0]);
+    }
+
+    public int gotoLeftCell() throws TreeManagerException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int gotoRightCell() throws TreeManagerException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int gotoUpCell() throws TreeManagerException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int gotoDownCell() throws TreeManagerException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int getActiveTableInfo() throws TreeManagerException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int getActiveTableCellInfo() throws TreeManagerException {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public ITreeItem getCurrentRootItem() throws TreeManagerException {
+        initNVM3Service();
+        ITreeItem parent, current;
+        current = getActiveItem();
+        while (true) {
+            parent = current.getParent();
+            if (parent == null)
+                break;
+            current = parent;
+        }
+        return current;
+    }
+
+    public ISoundControl getSoundControl() throws TreeManagerException {
+        initNVM3Service();
+        return nvm3Service.getSoundControl();
+    }
+
+    public IVideoControl getVideoControl() throws TreeManagerException {
+        initNVM3Service();
+        return nvm3Service.getVideoControl();
+    }
+
+    public IAccessKeyList getAccessKeyList() throws TreeManagerException {
+        initNVM3Service();
+        return nvm3Service.getAccessKeyList();
+    }
+
+    public int analyze() throws TreeManagerException {
+        initNVM3Service();
+        try {
+            return nvm3Service.analyze();
+        } catch (NVM3Exception e) {
+            throw new TreeManagerException(ITreeManager.ERROR, "Failed to analyze", e);
+        }
+    }
+
+    public ILocation getCurrentLocation() throws TreeManagerException {
+        initNVM3Service();
+        return new LocationImpl(getActivePos());
+    }
+
+    public int moveToLocation(ILocation location) throws TreeManagerException {
+        initNVM3Service();
+        LocationImpl loc = (LocationImpl) location;
+        int[] pos = loc.getPos();
+        return setActivePos(pos);
+    }
+
+    public ITreeItem expandWholeTree() throws TreeManagerException {
+        initNVM3Service();
+        try {
+            return nvm3Service.expandWholeTree();
+        } catch (NVM3Exception e) {
+            throw new TreeManagerException(ITreeManager.ERROR, "Failed to expand the whole tree", e);
+        }
+    }
+
+    public int skipToAnchor(String target) throws TreeManagerException {
+        initNVM3Service();
+        try {
+            return nvm3Service.skipToAnchor(target);
+        } catch (NVM3Exception e) {
+            throw new TreeManagerException(ITreeManager.ERROR, "Failed to find the target in the anchors", e);
+        }
+    }
+    
+    public void repairFlash() throws TreeManagerException {
+        initNVM3Service();
+        IFlashNode[] node = nvm3Service.getFlashTopNodes();
+        for (int i = 0; i < node.length; i++) {
+            node[i].repairFlash();
+        }
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.key.keyui/.classpath b/plugins/org.eclipse.actf.ai.key.keyui/.classpath
new file mode 100644
index 0000000..751c8f2
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.key.keyui/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/plugins/org.eclipse.actf.ai.key.keyui/.cvsignore b/plugins/org.eclipse.actf.ai.key.keyui/.cvsignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.key.keyui/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/plugins/org.eclipse.actf.ai.key.keyui/.project b/plugins/org.eclipse.actf.ai.key.keyui/.project
new file mode 100644
index 0000000..dde0dad
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.key.keyui/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.actf.ai.key.keyui</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>
diff --git a/plugins/org.eclipse.actf.ai.key.keyui/META-INF/MANIFEST.MF b/plugins/org.eclipse.actf.ai.key.keyui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..b8c2599
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.key.keyui/META-INF/MANIFEST.MF
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Eclipse ACTF Key UI Plug-in (Incubation)
+Bundle-SymbolicName: org.eclipse.actf.ai.key.keyui;singleton:=true
+Bundle-Version: 0.0.1
+Bundle-Activator: org.eclipse.actf.ai.key.keyui.KeyUIPlugin
+Bundle-Vendor: Eclipse.org
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.actf.ai.key.keyhook.win32,
+ org.eclipse.actf.ai.xmlstore,
+ org.eclipse.ui,
+ org.eclipse.actf.ai.navigator
+Eclipse-LazyStart: true
diff --git a/plugins/org.eclipse.actf.ai.key.keyui/about.html b/plugins/org.eclipse.actf.ai.key.keyui/about.html
new file mode 100644
index 0000000..481dbcf
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.key.keyui/about.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>June 5, 2006</p>	
+<h3>License</h3>
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  
+Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/org/documents/epl-v10.php">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor&rsquo;s license 
+that was provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/org.eclipse.actf.ai.key.keyui/build.properties b/plugins/org.eclipse.actf.ai.key.keyui/build.properties
new file mode 100644
index 0000000..8f739ba
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.key.keyui/build.properties
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 2007 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+			   about.html,\
+               .,\
+               plugin.xml
diff --git a/plugins/org.eclipse.actf.ai.key.keyui/plugin.xml b/plugins/org.eclipse.actf.ai.key.keyui/plugin.xml
new file mode 100644
index 0000000..c64fb80
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.key.keyui/plugin.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+   <extension
+         point="org.eclipse.actf.ai.navigator.Navigation">
+      <manipulator class="org.eclipse.actf.ai.key.keyui.impl.KeyUIImpl"/>
+   </extension>
+
+</plugin>
diff --git a/plugins/org.eclipse.actf.ai.key.keyui/src/messages.properties b/plugins/org.eclipse.actf.ai.key.keyui/src/messages.properties
new file mode 100644
index 0000000..640b39e
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.key.keyui/src/messages.properties
@@ -0,0 +1,239 @@
+###############################################################################
+# Copyright (c) 2007 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+openTab=Open
+closeTab=Close
+
+traverseDown=Next Node
+traverseUp=Previous Node
+click=Click
+nextLink=Next Link
+previousLink=Previous Link
+
+searchNext=Find Next
+searchPrevious=Find Previous
+
+treeTop=Top Node
+treeBottom=Last Node
+
+nextHeader=Next Header
+previousHeader=Previous Header
+nextObject=Next Object
+previousObject=Previous Object
+nextInputable=Next Inputable
+previousInputable=Previous Inputable
+
+nextListItem=Next Item of List
+previousListItem=Previous Item of List
+
+nextBlock=Next Text Block
+previousBlock=Previous Text Block
+
+selectNextNVM3=Select Fennec data
+forceRestart=Content re-Aanalyse
+toggleDescriptionEnable=Audio Description On/Off
+  
+showAccessKeyList=Show Accesskey List
+speakCurrentStatus=Speak Current Status
+speakMediaStatus=Speak Media Status
+
+nextTab=Next Tab
+prevTab=Previous Tab
+
+playMedia=Play Video
+stopMedia=Stop Video
+pauseMedia=Pause Video
+  
+muteMedia=Mute Volume
+volumeDownMedia=Volume Down
+volumeUpMedia=Volume Up
+
+launchBrowser=Open in Default Browser
+toggleLeftViewsShowing=Open / Close Tree View
+speakAll=Say all
+
+importMetadata=Import Fennec data...
+exportMetadata=Export User Annotation...
+exportAllMetadata=Export All User Annotations...
+
+nextMedia=Next Media
+previousMedia=Previous Media
+
+saveUserInfo=Save Annotation
+removeUserInfo=Remove All Annotation
+editAltText=Edit Alt. Text
+makeLandmark=Set / Unset a Landmark
+nextAlterable=Next Alterable Item
+previousAlterable=Previous Alterable Item
+
+navigateRefresh=Refresh
+forward=Forward
+backward=Backward
+enterBrowserAddress=Enter Address
+
+nextHeader1=Next Header Level 1
+nextHeader2=Next Header Level 2
+nextHeader3=Next Header Level 3
+nextHeader4=Next Header Level 4
+nextHeader5=Next Header Level 5
+nextHeader6=Next Header Level 6
+
+previousHeader1=Previous Header Level 1
+previousHeader2=Previous Header Level 2
+previousHeader3=Previous Header Level 3
+previousHeader4=Previous Header Level 4
+previousHeader5=Previous Header Level 5
+previousHeader6=Previous Header Level 6
+
+minimalVolumeDownMedia=Volume Down (minimum step)
+minimalVolumeUpMedia=Volume Up (minimum step)
+speechSpeedDown=Speech Speed Down
+speechSpeedUp=Speech Speed Up
+
+repairFlash=Repair Flash content
+
+Key.OR=or
+
+
+CMD_PASSTHROUGH=Pass Through
+
+CMD_NOOP=No Operation
+
+CMD_MUTEMEDIA=Mute Volume
+
+CMD_VOLUMEDOWNMEDIA=Volume Down
+
+CMD_VOLUMEUPMEDIA=Volume Up
+
+CMD_PREVIOUSTRACK=Previous Track
+
+CMD_NEXTTRACK=Next Track
+
+CMD_STOPMEDIA=Stop Video
+
+CMD_PLAYMEDIA=Play Video
+
+CMD_PAUSEMEDIA=Pause Video
+
+CMD_FASTREVERSE=Rewind Video
+
+CMD_FASTFORWARD=Forward Video
+
+CMD_SPEECHSPEEDUP=Speed Up Speech
+
+CMD_SPEECHSPEEDDOWN=Speed Down Speech
+
+CMD_TREELEFT=Tree Left
+
+CMD_TREERIGHT=Tree Right
+
+CMD_TREEUP=Tree Up
+
+CMD_TREEDOWN=Tree Down
+
+CMD_TREETOP=Tree Top
+
+CMD_TREEBOTTOM=Tree Bottom
+
+CMD_TRAVERSENODEDOWN=Next Node
+
+CMD_TRAVERSENODEUP=Previous Node
+
+CMD_TRAVERSEDOWN=Next Item
+
+CMD_TRAVERSEUP=Previous Item
+
+CMD_CLICK=Click
+
+CMD_CELLLEFT=Cell Left
+
+CMD_CELLRIGHT=Cell Right
+
+CMD_CELLUP=Cell Up
+
+CMD_CELLDOWN=Cell Down
+
+CMD_STARTAUTOPLAY=Auto Play
+
+CMD_ENDAUTOPLAY=Stop Auto Play
+
+CMD_AUTOPLAYMINSPEED=Min Speed Auto Play
+
+CMD_AUTOPLAYMAXSPEED=Max Speed Auto Play
+
+CMD_AUTOPLAYDEFAULTSPEED=Default Speed Auto Play
+
+CMD_AUTOPLAYNOP=Auto Play No Operation
+
+CMD_AUTOPLAYSLOWER=Slower Auto Play
+
+CMD_AUTOPLAYFASTER=Faster Auto Play
+
+CMD_NEXTHEADER=Next Header
+
+CMD_PREVIOUSHEADER=Previous Header
+
+CMD_NEXTINPUTABLE=Next Input
+
+CMD_PREVIOUSINPUTABLE=Previous Input
+
+CMD_HOME=Go to top of page
+
+CMD_END=Go to end of page
+
+CMD_SELECTNEXTNVM3=Select Fennec data
+
+CMD_ENTERBROWSERADDRESS=Edit Address
+
+CMD_FORCERESTART=Content re-Aanalyse
+
+CMD_NAVIGATEREFRESH=Refrefh
+
+CMD_SPEAKCURRENTSTATUS=Speak Current Status
+
+CMD_NEXTLINK=Next Link
+
+CMD_PREVIOUSLINK=Previous Link
+
+CMD_NEXTOBJECT=Next Object
+
+CMD_PREVIOUSOBJECT=Previous Object
+
+CMD_EXIT_FORM_MODE=Exit Form Mode
+
+CMD_FORM_IGNORE_KEY=Form Ignore Key
+
+CMD_FORM_SUBMIT=Form Submit
+
+CMD_TOGGLE_DESCRIPTION_ENABLE=Audio Description
+
+CMD_TAKE_BACK_CONTROL=Take Back Control
+
+CMD_CLOSETAB=Close Tab
+    
+CMD_NEXTTAB=Next Tab
+
+CMD_PREVTAB=Prev Tab
+    
+CMD_GO_FORWARD=Go Forward
+   
+CMD_GO_BACKWORD=Go Backward
+
+CMD_OPEN=Open
+
+CMD_SPEED_UP=Speed Up
+
+CMD_SPEED_DOWN=Speed Down
+
+CMD_PLAY_NEXT=Play Next
+
+CMD_PLAY_PREV=Play Prev
+    
+CMD_START_RECORDING=Start Recording
diff --git a/plugins/org.eclipse.actf.ai.key.keyui/src/messages_ja.properties b/plugins/org.eclipse.actf.ai.key.keyui/src/messages_ja.properties
new file mode 100644
index 0000000..14cf70f
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.key.keyui/src/messages_ja.properties
@@ -0,0 +1,239 @@
+###############################################################################
+# Copyright (c) 2007 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+openTab=\u958b\u304f
+closeTab=\u9589\u3058\u308b
+
+traverseDown=\u6b21\u306e\u9805\u76ee
+traverseUp=\u524d\u306e\u9805\u76ee
+click=\u30af\u30ea\u30c3\u30af
+nextLink=\u6b21\u306e\u30ea\u30f3\u30af
+previousLink=\u524d\u306e\u30ea\u30f3\u30af
+
+searchNext=\u6b21\u3092\u691c\u7d22
+searchPrevious=\u524d\u3092\u691c\u7d22
+
+treeTop=\u5148\u982d\u306e\u9805\u76ee\u3078
+treeBottom=\u6700\u5f8c\u306e\u9805\u76ee\u3078
+
+nextHeader=\u6b21\u306e\u898b\u51fa\u3057\u3078
+previousHeader=\u524d\u306e\u898b\u51fa\u3057\u3078
+nextObject=\u6b21\u306e\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3078
+previousObject=\u524d\u306e\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3078
+nextInputable=\u6b21\u306e\u30d5\u30a9\u30fc\u30e0\u3078
+previousInputable=\u524d\u306e\u30d5\u30a9\u30fc\u30e0\u3078
+
+nextListItem=\u6b21\u306e\u30ea\u30b9\u30c8\u9805\u76ee\u3078
+previousListItem=\u524d\u306e\u30ea\u30b9\u30c8\u9805\u76ee\u3078
+
+nextBlock=\u6b21\u306e\u30c6\u30ad\u30b9\u30c8\u30d6\u30ed\u30c3\u30af\u3078
+previousBlock=\u524d\u306e\u30c6\u30ad\u30b9\u30c8\u30d6\u30ed\u30c3\u30af\u3078
+
+selectNextNVM3=Fennec\u5916\u90e8\u60c5\u5831\u306e\u5207\u308a\u66ff\u3048
+forceRestart=\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u518d\u89e3\u6790
+toggleDescriptionEnable=\u97f3\u58f0\u89e3\u8aac\u306e\u30aa\u30f3\u30fb\u30aa\u30d5
+  
+showAccessKeyList=\u30a2\u30af\u30bb\u30b9\u30ad\u30fc\u306e\u30ea\u30b9\u30c8\u3092\u8868\u793a
+speakCurrentStatus=\u73fe\u5728\u306e\u30b9\u30c6\u30fc\u30bf\u30b9\u3092\u8aad\u4e0a\u3052\u308b
+speakMediaStatus=\u30e1\u30c7\u30a3\u30a2\u306e\u30b9\u30c6\u30fc\u30bf\u30b9\u3092\u8aad\u4e0a\u3052\u308b
+
+nextTab=\u6b21\u306e\u30bf\u30d6
+prevTab=\u524d\u306e\u30bf\u30d6
+
+playMedia=\u30d3\u30c7\u30aa\u306e\u518d\u751f
+stopMedia=\u30d3\u30c7\u30aa\u306e\u505c\u6b62
+pauseMedia=\u30d3\u30c7\u30aa\u306e\u4e00\u6642\u505c\u6b62
+  
+muteMedia=\u6d88\u97f3
+volumeDownMedia=\u97f3\u91cf\u5c0f
+volumeUpMedia=\u97f3\u91cf\u5927
+
+launchBrowser=\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u30d6\u30e9\u30a6\u30b6\u3067\u958b\u304f
+toggleLeftViewsShowing=\u30c4\u30ea\u30fc\u30d3\u30e5\u30fc\u3092\u958b\u304f\uff0f\u9589\u3058\u308b
+speakAll=\u5168\u3066\u8aad\u4e0a\u3052
+
+importMetadata=Fennec \u30c7\u30fc\u30bf\u306e\u8aad\u307f\u8fbc\u307f...
+exportMetadata=\u6ce8\u91c8\u30c7\u30fc\u30bf\u306e\u66f8\u304d\u51fa\u3057...
+exportAllMetadata=\u5168\u3066\u306e\u6ce8\u91c8\u30c7\u30fc\u30bf\u306e\u66f8\u304d\u51fa\u3057...
+
+nextMedia=\u6b21\u306e\u30e1\u30c7\u30a3\u30a2
+previousMedia=\u524d\u306e\u30e1\u30c7\u30a3\u30a2
+
+saveUserInfo=\u6ce8\u91c8\u306e\u30bb\u30fc\u30d6
+removeUserInfo=\u6ce8\u91c8\u3092\u5168\u3066\u524a\u9664
+editAltText=\u4ee3\u66ff\u30c6\u30ad\u30b9\u30c8\u306e\u7de8\u96c6
+makeLandmark=\u3057\u304a\u308a\u306e\u8ffd\u52a0\uff0f\u524a\u9664
+nextAlterable=\u6b21\u306e\u4ee3\u66ff\u30c6\u30ad\u30b9\u30c8\u7de8\u96c6\u70b9
+previousAlterable=\u524d\u306e\u4ee3\u66ff\u30c6\u30ad\u30b9\u30c8\u7de8\u96c6\u70b9
+
+navigateRefresh=\u6700\u65b0\u306e\u60c5\u5831\u306b\u66f4\u65b0
+forward=\u9032\u3080
+backward=\u623b\u308b
+enterBrowserAddress=\u30a2\u30c9\u30ec\u30b9\u5165\u529b
+
+nextHeader1=\u6b21\u306e\u30ec\u30d9\u30eb\uff11\u306e\u898b\u51fa\u3057
+nextHeader2=\u6b21\u306e\u30ec\u30d9\u30eb\uff12\u306e\u898b\u51fa\u3057
+nextHeader3=\u6b21\u306e\u30ec\u30d9\u30eb\uff13\u306e\u898b\u51fa\u3057
+nextHeader4=\u6b21\u306e\u30ec\u30d9\u30eb\uff14\u306e\u898b\u51fa\u3057
+nextHeader5=\u6b21\u306e\u30ec\u30d9\u30eb\uff15\u306e\u898b\u51fa\u3057
+nextHeader6=\u6b21\u306e\u30ec\u30d9\u30eb\uff16\u306e\u898b\u51fa\u3057
+
+previousHeader1=\u524d\u306e\u30ec\u30d9\u30eb\uff11\u306e\u898b\u51fa\u3057
+previousHeader2=\u524d\u306e\u30ec\u30d9\u30eb\uff12\u306e\u898b\u51fa\u3057
+previousHeader3=\u524d\u306e\u30ec\u30d9\u30eb\uff13\u306e\u898b\u51fa\u3057
+previousHeader4=\u524d\u306e\u30ec\u30d9\u30eb\uff14\u306e\u898b\u51fa\u3057
+previousHeader5=\u524d\u306e\u30ec\u30d9\u30eb\uff15\u306e\u898b\u51fa\u3057
+previousHeader6=\u524d\u306e\u30ec\u30d9\u30eb\uff16\u306e\u898b\u51fa\u3057
+
+minimalVolumeDownMedia=\u97f3\u91cf\u5c0f\uff08\u6700\u5c0f\u5e45\uff09
+minimalVolumeUpMedia=\u97f3\u91cf\u5927\uff08\u6700\u5c0f\u5e45\uff09
+speechSpeedDown=\u8aad\u4e0a\u3052\u901f\u5ea6\u9045\u304f
+speechSpeedUp=\u8aad\u4e0a\u3052\u901f\u5ea6\u901f\u304f
+
+repairFlash=Flash\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u4fee\u5fa9
+
+Key.OR=\u3082\u3057\u304f\u306f
+
+CMD_PASSTHROUGH=\u901a\u904e
+
+CMD_NOOP=\u64cd\u4f5c\u306a\u3057
+
+CMD_MUTEMEDIA=\u6d88\u97f3
+
+CMD_VOLUMEDOWNMEDIA=\u97f3\u91cf\u5927
+
+CMD_VOLUMEUPMEDIA=\u97f3\u91cf\u5c0f
+
+CMD_PREVIOUSTRACK=\u524d\u306e\u30c8\u30e9\u30c3\u30af\u3078
+
+CMD_NEXTTRACK=\u6b21\u306e\u30c8\u30e9\u30c3\u30af\u3078
+
+CMD_STOPMEDIA=\u30d3\u30c7\u30aa\u505c\u6b62
+
+CMD_PLAYMEDIA=\u30d3\u30c7\u30aa\u518d\u751f
+
+CMD_PAUSEMEDIA=\u30d3\u30c7\u30aa\u4e00\u6642\u505c\u6b62
+
+CMD_FASTREVERSE=\u30d3\u30c7\u30aa\u5dfb\u304d\u623b\u3057
+
+CMD_FASTFORWARD=\u30d3\u30c7\u30aa\u65e9\u9001\u308a
+
+CMD_SPEECHSPEEDUP=\u8aad\u4e0a\u3052\u901f\u5ea6\u901f\u304f
+
+CMD_SPEECHSPEEDDOWN=\u8aad\u4e0a\u3052\u901f\u5ea6\u9045\u304f
+
+CMD_TREELEFT=\u30c4\u30ea\u30fc\u5de6\u3078
+
+CMD_TREERIGHT=\u30c4\u30ea\u30fc\u53f3\u3078
+
+CMD_TREEUP=\u30c4\u30ea\u30fc\u4e0a\u3078
+
+CMD_TREEDOWN=\u30c4\u30ea\u30fc\u4e0b\u3078
+
+CMD_TREETOP=\u30c4\u30ea\u30fc\u6700\u521d\u3078
+
+CMD_TREEBOTTOM=\u30c4\u30ea\u30fc\u6700\u5f8c\u3078
+
+CMD_TRAVERSENODEDOWN=\u6b21\u306e\u30ce\u30fc\u30c9\u3078
+
+CMD_TRAVERSENODEUP=\u524d\u306e\u30ce\u30fc\u30c9\u3078
+
+CMD_TRAVERSEDOWN=\u6b21\u306e\u9805\u76ee\u3078
+
+CMD_TRAVERSEUP=\u524d\u306e\u9805\u76ee\u3078
+
+CMD_CLICK=\u30af\u30ea\u30c3\u30af
+
+CMD_CELLLEFT=\u5de6\u306e\u30bb\u30eb\u3078
+
+CMD_CELLRIGHT=\u53f3\u306e\u30bb\u30eb\u3078
+
+CMD_CELLUP=\u4e0a\u306e\u30bb\u30eb\u3078
+
+CMD_CELLDOWN=\u4e0b\u306e\u30bb\u30eb\u3078
+
+CMD_STARTAUTOPLAY=\u81ea\u52d5\u8aad\u4e0a\u3052
+
+CMD_ENDAUTOPLAY=\u81ea\u52d5\u8aad\u4e0a\u3052\u505c\u6b62
+
+CMD_AUTOPLAYMINSPEED=\u81ea\u52d5\u8aad\u4e0a\u3052\u3001\u6700\u5c0f\u901f\u5ea6
+
+CMD_AUTOPLAYMAXSPEED=\u81ea\u52d5\u8aad\u4e0a\u3052\u3001\u6700\u5927\u901f\u5ea6
+
+CMD_AUTOPLAYDEFAULTSPEED=\u81ea\u52d5\u8aad\u4e0a\u3052\u3001\u901a\u5e38\u901f\u5ea6
+
+CMD_AUTOPLAYNOP=\u81ea\u52d5\u8aad\u4e0a\u3052\u3001\u64cd\u4f5c\u306a\u3057
+
+CMD_AUTOPLAYSLOWER=\u81ea\u52d5\u8aad\u4e0a\u3052\u3001\u901f\u5ea6\u9045\u304f
+
+CMD_AUTOPLAYFASTER=\u81ea\u52d5\u8aad\u4e0a\u3052\u3001\u901f\u5ea6\u901f\u304f
+
+CMD_NEXTHEADER=\u6b21\u306e\u30d8\u30c3\u30c0\u30fc\u3078
+
+CMD_PREVIOUSHEADER=\u524d\u306e\u30d8\u30c3\u30c0\u30fc\u3078
+
+CMD_NEXTINPUTABLE=\u6b21\u306e\u30d5\u30a9\u30fc\u30e0\u3078
+
+CMD_PREVIOUSINPUTABLE=\u524d\u306e\u30d5\u30a9\u30fc\u30e0\u3078
+
+CMD_HOME=\u30da\u30fc\u30b8\u306e\u5148\u982d\u3078
+
+CMD_END=\u30da\u30fc\u30b8\u306e\u6700\u5f8c\u3078
+
+CMD_SELECTNEXTNVM3=Fennec \u30c7\u30fc\u30bf\u306e\u5207\u308a\u66ff\u3048
+
+CMD_ENTERBROWSERADDRESS=\u30a2\u30c9\u30ec\u30b9\u5165\u529b
+
+CMD_FORCERESTART=\u30b3\u30f3\u30c6\u30f3\u30c4\u306e\u518d\u89e3\u6790
+
+CMD_NAVIGATEREFRESH=\u30ea\u30d5\u30ec\u30c3\u30b7\u30e5
+
+CMD_SPEAKCURRENTSTATUS=\u73fe\u5728\u306e\u30b9\u30c6\u30fc\u30bf\u30b9\u3092\u8aad\u4e0a\u3052\u308b
+
+CMD_NEXTLINK=\u6b21\u306e\u30ea\u30f3\u30af\u3078
+
+CMD_PREVIOUSLINK=\u524d\u306e\u30ea\u30f3\u30af\u3078
+
+CMD_NEXTOBJECT=\u6b21\u306e\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3078
+
+CMD_PREVIOUSOBJECT=\u524d\u306e\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3078
+
+CMD_EXIT_FORM_MODE=\u30d5\u30a9\u30fc\u30e0\u30e2\u30fc\u30c9\u7d42\u4e86
+
+CMD_FORM_IGNORE_KEY=Form Ignore Key
+
+CMD_FORM_SUBMIT=\u9001\u4fe1\u30dc\u30bf\u30f3
+
+CMD_TOGGLE_DESCRIPTION_ENABLE=\u97f3\u58f0\u89e3\u8aac
+
+CMD_TAKE_BACK_CONTROL=\u30c6\u30a4\u30af\u30d0\u30c3\u30af\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb
+
+CMD_CLOSETAB=\u30bf\u30d6\u3092\u9589\u3058\u308b
+    
+CMD_NEXTTAB=\u6b21\u306e\u30bf\u30d6\u3078
+
+CMD_PREVTAB=\u524d\u306e\u30bf\u30d6\u3078
+    
+CMD_GO_FORWARD=\u9032\u3080
+ 
+CMD_GO_BACKWORD=\u623b\u308b
+
+CMD_OPEN=\u958b\u304f
+
+CMD_SPEED_UP=\u901f\u5ea6\u901f\u304f
+
+CMD_SPEED_DOWN=\u901f\u5ea6\u9045\u304f
+
+CMD_PLAY_NEXT=\u6b21\u3092\u518d\u751f
+
+CMD_PLAY_PREV=\u524d\u3092\u518d\u751f
+    
+CMD_START_RECORDING=\u9332\u97f3\u958b\u59cb
+
diff --git a/plugins/org.eclipse.actf.ai.key.keyui/src/org/eclipse/actf/ai/key/keyui/KeyUIPlugin.java b/plugins/org.eclipse.actf.ai.key.keyui/src/org/eclipse/actf/ai/key/keyui/KeyUIPlugin.java
new file mode 100644
index 0000000..8d60c3a
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.key.keyui/src/org/eclipse/actf/ai/key/keyui/KeyUIPlugin.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.key.keyui;
+
+import org.eclipse.core.runtime.Plugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The main plugin class to be used in the desktop.
+ */
+public class KeyUIPlugin extends Plugin {
+
+    //The shared instance.
+    private static KeyUIPlugin plugin;
+	
+    /**
+     * The constructor.
+     */
+    public KeyUIPlugin() {
+        plugin = this;
+    }
+
+    /**
+     * This method is called upon plug-in activation
+     */
+    @Override
+    public void start(BundleContext context) throws Exception {
+        super.start(context);
+    }
+
+    /**
+     * This method is called when the plug-in is stopped
+     */
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        super.stop(context);
+        plugin = null;
+    }
+
+    /**
+     * Returns the shared instance.
+     */
+    public static KeyUIPlugin getDefault() {
+        return plugin;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.key.keyui/src/org/eclipse/actf/ai/key/keyui/Messages.java b/plugins/org.eclipse.actf.ai.key.keyui/src/org/eclipse/actf/ai/key/keyui/Messages.java
new file mode 100644
index 0000000..fa1ebaf
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.key.keyui/src/org/eclipse/actf/ai/key/keyui/Messages.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Takashi ITOH - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.key.keyui;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+
+
+public class Messages {
+    private static final String BUNDLE_NAME = "messages"; //$NON-NLS-1$
+
+    private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);
+
+    private Messages() {
+    }
+
+    public static String getString(String key) {
+        // TODO Auto-generated method stub
+        try {
+            return RESOURCE_BUNDLE.getString(key);
+        } catch (MissingResourceException e) {
+            return '!' + key + '!';
+        }
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.key.keyui/src/org/eclipse/actf/ai/key/keyui/impl/KeyUIImpl.java b/plugins/org.eclipse.actf.ai.key.keyui/src/org/eclipse/actf/ai/key/keyui/impl/KeyUIImpl.java
new file mode 100644
index 0000000..c2ce68e
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.key.keyui/src/org/eclipse/actf/ai/key/keyui/impl/KeyUIImpl.java
@@ -0,0 +1,528 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.key.keyui.impl;
+
+import java.awt.event.KeyEvent;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.eclipse.actf.ai.key.keyhook.win32.IKeyHook;
+import org.eclipse.actf.ai.key.keyhook.win32.IKeyHookListener;
+import org.eclipse.actf.ai.key.keyhook.win32.ISendEvent;
+import org.eclipse.actf.ai.key.keyhook.win32.KeyHookWin32Plugin;
+import org.eclipse.actf.ai.key.keyui.Messages;
+import org.eclipse.actf.ai.key.keyui.impl.KeyUIImpl.KC.Type;
+import org.eclipse.actf.ai.navigator.IBrowserControl;
+import org.eclipse.actf.ai.navigator.IManipulator;
+import org.eclipse.actf.ai.navigator.INavigatorUI;
+import org.eclipse.actf.ai.navigator.NavigatorPlugin;
+import org.eclipse.actf.ai.xmlstore.IXMLInfo;
+import org.eclipse.actf.ai.xmlstore.IXMLSelector;
+import org.eclipse.actf.ai.xmlstore.IXMLStore;
+import org.eclipse.actf.ai.xmlstore.IXMLStoreService;
+import org.eclipse.actf.ai.xmlstore.XMLStoreException;
+import org.eclipse.actf.ai.xmlstore.XMLStorePlugin;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.Separator;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+
+
+
+public class KeyUIImpl implements IKeyHookListener, IManipulator {
+    private static boolean initialized = false;
+
+    public static final String PREFERENCES_NS = "http://www.ibm.com/xmlns/prod/AcTF/aiBrowser/preferences/1.0";
+
+    public static final int CMD_PASSTHROUGH = -1;
+
+    public static final int CMD_NOOP = 9999;
+
+    // for demo
+    public static final int CMD_SPEED_UP = 1300;
+
+    public static final int CMD_SPEED_DOWN = 1301;
+
+    public static final int CMD_PLAY_NEXT = 1302;
+
+    public static final int CMD_PLAY_PREV = 1303;
+
+    public static final int CMD_START_RECORDING = 1304;
+
+    private static final int VK_RETURN = 13;
+
+    private IManipulator.Mode mode = null;
+
+    private IBrowserControl browserControl;
+
+    private INavigatorUI navigatorUI;
+
+    private HashMap<KeyEntry, KC> keyConfigMapForTreeNavigation = new HashMap<KeyEntry, KC>();
+
+    private static IKeyHook keyHook;
+
+    private static ISendEvent sendEvent;
+
+    public KeyUIImpl() {
+        if (!initialized) {
+            initialized = true;
+            initialize();
+        }
+    }
+
+    public void initialize() {
+        keyHook = KeyHookWin32Plugin.getDefault().newKeyHook(this);
+        sendEvent = KeyHookWin32Plugin.getDefault().newSendEvent();
+
+        IXMLStoreService xmlStoreService = XMLStorePlugin.getDefault().getXMLStoreService();
+        IXMLSelector selector = xmlStoreService.getSelectorWithDocElem("UserPreferences", PREFERENCES_NS);
+        IXMLStore rootStore = xmlStoreService.getRootStore();
+        IXMLStore specifiedStroe = rootStore.specify(selector);
+        for (Iterator<IXMLInfo> i = specifiedStroe.getInfoIterator(); i.hasNext();) {
+            IXMLInfo info = i.next();
+            try {
+                read(info.getRootNode());
+            } catch (XMLStoreException e) {
+                e.printStackTrace();
+            }
+        }
+        
+        initAccessKey();
+    }
+    
+    private void initAccessKey() {
+        for (int i = 0; i < 10; i++) {
+            KC kc = new KC(KC.Type.ACCESSKEY, KeyEvent.VK_0 + i, KeyEvent.SHIFT_MASK | KeyEvent.ALT_MASK, null);
+            register(kc);
+        }
+        for (int i = 0; i < 26; i++) {
+            KC kc = new KC(KC.Type.ACCESSKEY, KeyEvent.VK_A + i, KeyEvent.SHIFT_MASK | KeyEvent.ALT_MASK, null);
+            register(kc);
+        }
+    }
+
+    private void read(Node rootNode) {
+        NodeList children = rootNode.getChildNodes();
+
+        for (int i = 0; i < children.getLength(); i++) {
+            Node commands = children.item(i);
+            KC.Type type = KC.Type.COMMAND;
+
+            if (commands.getNodeType() != Node.ELEMENT_NODE)
+                continue;
+
+            if (commands.getLocalName().equals("Commands")) {
+                type = KC.Type.COMMAND;
+            } else if (commands.getLocalName().equals("Functions")) {
+                type = KC.Type.FUNCTION;
+            } else {
+                continue;
+            }
+
+            NodeList commandList = commands.getChildNodes();
+
+            for (int j = 0; j < commandList.getLength(); j++) {
+                Node command = commandList.item(j);
+
+                if (command.getNodeType() != Node.ELEMENT_NODE)
+                    continue;
+
+                NodeList keyList = command.getChildNodes();
+
+                ArrayList<KC> array = new ArrayList<KC>();
+                for (int k = 0; k < keyList.getLength(); k++) {
+                    Node key = keyList.item(k);
+
+                    if (key.getNodeType() != Node.ELEMENT_NODE)
+                        continue;
+
+                    KC kc = createKC(type, command, key);
+                    register(kc);
+
+                    if (!"none".equals(((Element) key).getAttribute("display")))
+                        array.add(kc);
+                }
+                addMenu(commands, command, array);
+            }
+        }
+    }
+
+    private KC createKC(Type type, Node command, Node key) {
+        String name = command.getLocalName();
+        int vkey = 0;
+        int mod = 0;
+        if ("key".equals(key.getLocalName()) && PREFERENCES_NS.equals(key.getNamespaceURI())) {
+            String stroke = key.getTextContent();
+            stroke = stroke.trim();
+            String[] keys = stroke.split("[ \n\t\r]+");
+            for (int j = 0; j < keys.length; j++) {
+                try {
+                    if ("VK_RETURN".equals(keys[j])) {
+                        vkey = VK_RETURN;
+                    } else if (keys[j].startsWith("VK_")) {
+                        Field f = KeyEvent.class.getField(keys[j]);
+                        vkey = f.getInt(KeyEvent.class);
+                    } else if (keys[j].endsWith("_MASK")) {
+                        Field f = KeyEvent.class.getField(keys[j]);
+                        mod |= f.getInt(KeyEvent.class);
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return new KC(type, vkey, mod, name);
+    }
+
+    private void addMenu(Node commands, Node command, ArrayList<KC> kc) {
+        String menuName = ((Element) commands).getAttribute("menu");
+        if (menuName.length() == 0)
+            return;
+        String groupName = ((Element) commands).getAttribute("menuGroup");
+        if (groupName.length() == 0)
+            return;
+        String name = command.getLocalName();
+
+        IMenuManager menu = NavigatorPlugin.menuManager;
+        IMenuManager menu2 = menu.findMenuUsingPath(menuName);
+
+        if ("separator".equals(name)) {
+            menu2.appendToGroup(groupName, new Separator());
+        } else {
+            menu2.appendToGroup(groupName, new KeyAction(name, kc));
+        }
+    }
+
+    static class KeyEntry {
+        int vkey;
+
+        int mod;
+
+        @Override
+        public int hashCode() {
+            return (vkey | mod << 16);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof KeyEntry))
+                return false;
+            KeyEntry e = (KeyEntry) o;
+            return ((e.vkey == vkey) && (e.mod == mod));
+        }
+
+        KeyEntry(int vkey, int mod) {
+            this.vkey = vkey;
+            this.mod = mod;
+        }
+    }
+
+    private void register(KC kc) {
+        keyConfigMapForTreeNavigation.put(new KeyEntry(kc.vkey, kc.mod), kc);
+        keyHook.registerHookedKey(kc.vkey, kc.mod);
+    }
+
+    static class KC {
+        enum Type {
+            COMMAND, FUNCTION, NOOP, ACCESSKEY
+        }
+
+        int vkey;
+
+        int mod;
+
+        Type type;
+
+        String name;
+        
+        enum Target {
+            INAVIGATORUI, IBROWSERCONTROL
+        }
+        
+        Target target;
+
+        Method method;
+
+        int command;
+        
+        private Method lookupMethod(Class clazz, String name) {
+            try {
+                return clazz.getMethod(name, new Class[0]);
+            } catch (Exception e) {
+                return null;
+            }
+        }
+
+        KC(Type type, int vkey, int mod, String name) {
+            this.type = type;
+            this.vkey = vkey;
+            this.mod = mod;
+            this.name = name;
+
+            if (type == Type.FUNCTION) {
+                method = lookupMethod(INavigatorUI.class, name);
+                if (method != null) {
+                    target = Target.INAVIGATORUI;
+                } else {
+                    method = lookupMethod(IBrowserControl.class, name);
+                    if (method == null) {
+                        System.err.println("No such method:" + name);
+                    }
+                    target = Target.IBROWSERCONTROL;
+                }
+            } else if (type == Type.COMMAND) {
+                try {
+                    Field field = KeyUIImpl.class.getField("CMD_" + name);
+                    command = field.getInt(KeyUIImpl.class);
+                } catch (SecurityException e) {
+                    e.printStackTrace();
+                } catch (NoSuchFieldException e) {
+                    e.printStackTrace();
+                } catch (IllegalArgumentException e) {
+                    e.printStackTrace();
+                } catch (IllegalAccessException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    public boolean hookedKey(int vkey, int modifier, boolean isUp) {
+        if (mode == IManipulator.TREE_NAVIGATION_MODE) {
+            KC kc = (KC) keyConfigMapForTreeNavigation.get(new KeyEntry(vkey, modifier));
+            if (kc != null) {
+                if (kc.type == KC.Type.COMMAND && kc.command == CMD_NOOP)
+                    return true;
+                if (kc.type != KC.Type.ACCESSKEY && kc.method == null)
+                    return false;
+                
+                /*
+                if (vkey == VK_RETURN) {
+                    if (!isUp)
+                        call(kc);
+                } else {
+                    if (isUp)
+                        call(kc);
+                }*/
+                if (!isUp)
+                    call(kc);
+
+                return true;
+            }
+        } else if (mode == IManipulator.FORM_INPUT_MODE) {
+            switch (vkey) {
+            case KeyEvent.VK_TAB:
+                break;
+            case VK_RETURN:
+                // navigatorUI.submitForm();
+                sendEvent.postKey(vkey, isUp);
+                break;
+            case KeyEvent.VK_ESCAPE:
+                navigatorUI.exitFormMode();
+                break;
+            default:
+                return false;
+            }
+            return true;
+        } else if (mode == IManipulator.KEYHOOK_DISABLED_MODE) {
+            return false;
+        }
+        return false;
+    }
+
+    private void call(KC kc) {
+        if (kc.type == KC.Type.COMMAND) {
+            switch (kc.command) {
+            // for demo
+            case CMD_SPEED_UP:
+                sendKey(KeyEvent.VK_UP, true);
+                break;
+            case CMD_SPEED_DOWN:
+                sendKey(KeyEvent.VK_DOWN, true);
+                break;
+            case CMD_PLAY_NEXT:
+                sendKey(KeyEvent.VK_RIGHT, false);
+                break;
+            case CMD_PLAY_PREV:
+                sendKey(KeyEvent.VK_LEFT, false);
+                break;
+            case CMD_START_RECORDING:
+                sendKey(KeyEvent.VK_S, true);
+                break;
+            case CMD_NOOP:
+            case CMD_PASSTHROUGH:
+                break;
+            }
+        } else if (kc.type == KC.Type.FUNCTION) {
+            try {
+                switch (kc.target) {
+                case IBROWSERCONTROL:
+                    if (browserControl != null)
+                        kc.method.invoke(browserControl, new Object[0]);
+                    break;
+                case INAVIGATORUI:
+                    if (navigatorUI != null)
+                        kc.method.invoke(navigatorUI, new Object[0]);
+                    break;
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        } else if (kc.type == KC.Type.ACCESSKEY) {
+            navigatorUI.jumpToAccessKey((char) kc.vkey);
+        }
+    }
+
+    private void sendKey(int key, boolean ctrlFlag) {
+        long h = sendEvent.findWindow("SWT_Window0", "Sound Controller");
+        if (ctrlFlag) {
+            sendEvent.postKeyToWindow(h, KeyEvent.VK_CONTROL, false);
+        }
+        sendEvent.postKeyToWindow(h, key, false);
+        sendEvent.postKeyToWindow(h, key, true);
+        if (ctrlFlag) {
+            sendEvent.postKeyToWindow(h, KeyEvent.VK_CONTROL, true);
+        }
+    }
+
+    public void setNavigator(INavigatorUI navigatorUI) {
+        this.navigatorUI = navigatorUI;
+    }
+
+    public void setBrowserControl(IBrowserControl browserControl) {
+        this.browserControl = browserControl;
+    }
+
+    public void dispose() {
+        keyHook.dispose();
+    }
+
+    public void setMode(Mode mode) {
+        this.mode = mode;
+        if (mode == IManipulator.FORM_INPUT_MODE) {
+            keyHook.hookAll(true);
+        } else if (mode == IManipulator.KEYHOOK_DISABLED_MODE) {
+            keyHook.hookAll(false);
+        } else {
+            keyHook.hookAll(false);
+        }
+    }
+
+    class KeyAction extends Action {
+        String name;
+
+        ArrayList<KC> kcs;
+
+        public KeyAction(String name, ArrayList<KC> kcs) {
+            super();
+            this.name = name;
+            this.kcs = kcs;
+
+            StringBuffer sb = new StringBuffer();
+            sb.append(Messages.getString(name));
+            if (kcs.size() > 0)
+                sb.append("    (" + getKey(kcs) + ")");
+
+            setText(sb.toString());
+        }
+
+        private String getKey(ArrayList<KC> kcs) {
+            StringBuffer sb = new StringBuffer();
+
+            for (int i = 0; i < kcs.size(); i++) {
+                if (i != 0)
+                    sb.append(" " + Messages.getString("Key.OR") + " ");
+
+                int vkey = kcs.get(i).vkey;
+                int mod = kcs.get(i).mod;
+
+                if (is(mod, KeyEvent.CTRL_MASK)) {
+                    sb.append("Ctrl+");
+                }
+                if (is(mod, KeyEvent.ALT_MASK)) {
+                    sb.append("Alt+");
+                }
+                if (is(mod, KeyEvent.SHIFT_MASK)) {
+                    sb.append("Shift+");
+                }
+                String key = "";
+                if (('0' <= vkey && vkey <= '9') || ('A' <= vkey && vkey <= 'Z')) {
+                    key = "" + (char) vkey;
+                } else if (KeyEvent.VK_F1 <= vkey && vkey <= KeyEvent.VK_F24) {
+                    key = "F" + (vkey - KeyEvent.VK_F1 + 1);
+                } else {
+                    switch (vkey) {
+                    case VK_RETURN:
+                        key = "Enter";
+                        break;
+                    case KeyEvent.VK_UP:
+                        key = "Up";
+                        break;
+                    case KeyEvent.VK_DOWN:
+                        key = "Down";
+                        break;
+
+                    case KeyEvent.VK_LEFT:
+                        key = "Left";
+                        break;
+                    case KeyEvent.VK_RIGHT:
+                        key = "Right";
+                        break;
+
+                    case KeyEvent.VK_HOME:
+                        key = "Home";
+                        break;
+                    case KeyEvent.VK_END:
+                        key = "End";
+                        break;
+                    case KeyEvent.VK_PAGE_UP:
+                        key = "PageUp";
+                        break;
+                    case KeyEvent.VK_PAGE_DOWN:
+                        key = "PageDown";
+                        break;
+
+                    case KeyEvent.VK_TAB:
+                        key = "Tab";
+                        break;
+                    case KeyEvent.VK_PAUSE:
+                        key = "Pause";
+                        break;
+                    case KeyEvent.VK_SPACE:
+                        key = "Space";
+                        break;
+                    }
+                }
+                sb.append(key);
+            }
+
+            return sb.toString();
+        }
+
+        private boolean is(int a, int b) {
+            return (a & b) == b;
+        }
+
+        @Override
+        public void run() {
+            if (kcs.size() > 0)
+                call(kcs.get(0));
+        }
+    }
+
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/.classpath b/plugins/org.eclipse.actf.ai.navigator/.classpath
new file mode 100644
index 0000000..751c8f2
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/plugins/org.eclipse.actf.ai.navigator/.cvsignore b/plugins/org.eclipse.actf.ai.navigator/.cvsignore
new file mode 100644
index 0000000..ba077a4
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/.cvsignore
@@ -0,0 +1 @@
+bin
diff --git a/plugins/org.eclipse.actf.ai.navigator/.project b/plugins/org.eclipse.actf.ai.navigator/.project
new file mode 100644
index 0000000..9821f58
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.actf.ai.navigator</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>
diff --git a/plugins/org.eclipse.actf.ai.navigator/.settings/org.eclipse.core.resources.prefs b/plugins/org.eclipse.actf.ai.navigator/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..e613298
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Wed Jun 06 23:23:23 JST 2007
+eclipse.preferences.version=1
+encoding//src/messages.properties=8859_1
diff --git a/plugins/org.eclipse.actf.ai.navigator/META-INF/MANIFEST.MF b/plugins/org.eclipse.actf.ai.navigator/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..3d3053b
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/META-INF/MANIFEST.MF
@@ -0,0 +1,21 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Eclipse ACTF Navigator Plug-in (Incubation)
+Bundle-SymbolicName: org.eclipse.actf.ai.navigator;singleton:=true
+Bundle-Version: 0.0.1
+Bundle-Activator: org.eclipse.actf.ai.navigator.NavigatorPlugin
+Bundle-Vendor: Eclipse.org
+Bundle-Localization: plugin
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.ui,
+ org.eclipse.actf.common,
+ org.eclipse.actf.ai.fennec,
+ org.eclipse.actf.util.vocab,
+ org.eclipse.actf.model.ui.editors.ie,
+ org.eclipse.actf.ai.xmlstore,
+ org.eclipse.actf.ai.audio.io,
+ org.eclipse.actf.ai.voice
+Eclipse-LazyStart: true
+Export-Package: org.eclipse.actf.ai.navigator,
+ org.eclipse.actf.ai.navigator.ui,
+ org.eclipse.actf.ai.navigator.views
diff --git a/plugins/org.eclipse.actf.ai.navigator/about.html b/plugins/org.eclipse.actf.ai.navigator/about.html
new file mode 100644
index 0000000..481dbcf
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/about.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>June 5, 2006</p>	
+<h3>License</h3>
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  
+Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/org/documents/epl-v10.php">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor&rsquo;s license 
+that was provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
+</body>
+</html>
\ No newline at end of file
diff --git a/plugins/org.eclipse.actf.ai.navigator/build.properties b/plugins/org.eclipse.actf.ai.navigator/build.properties
new file mode 100644
index 0000000..bb3f22a
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/build.properties
@@ -0,0 +1,20 @@
+###############################################################################
+# Copyright (c) 2007 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     IBM Corporation - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+			   about.html,\
+               .,\
+               plugin.xml,\
+               plugin.properties,\
+               plugin_ja.properties,\
+               icons/,\
+               waiting.wav
diff --git a/plugins/org.eclipse.actf.ai.navigator/icons/treex16.gif b/plugins/org.eclipse.actf.ai.navigator/icons/treex16.gif
new file mode 100644
index 0000000..dd54021
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/icons/treex16.gif
Binary files differ
diff --git a/plugins/org.eclipse.actf.ai.navigator/icons/treex48.gif b/plugins/org.eclipse.actf.ai.navigator/icons/treex48.gif
new file mode 100644
index 0000000..ffe678f
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/icons/treex48.gif
Binary files differ
diff --git a/plugins/org.eclipse.actf.ai.navigator/plugin.properties b/plugins/org.eclipse.actf.ai.navigator/plugin.properties
new file mode 100644
index 0000000..862084d
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/plugin.properties
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 2007 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     Daisuke SATO - initial configuration.
+###############################################################################
+
+DeleteAction.name=Clear metadata cache
+DeleteAction.tip=Clear metadata cache
+
+NavigationTreeView.name=Navigation by Tree View
+UserAnnotation=User Annotation
\ No newline at end of file
diff --git a/plugins/org.eclipse.actf.ai.navigator/plugin.xml b/plugins/org.eclipse.actf.ai.navigator/plugin.xml
new file mode 100644
index 0000000..7b4bc16
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/plugin.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+   <extension-point id="Navigation" name="Navigation" schema="schema/Navigation.exsd"/>
+   <extension-point id="ScreenReaderController" name="ScreenReaderController" schema="schema/ScreenReaderController.exsd"/>
+   <extension-point id="MediaControl" name="MediaControl" schema="schema/MediaControl.exsd"/>
+   <extension-point id="MetadataCacheCleaner" name="MetadataCacheCleaner" schema="schema/MetadataCacheCleaner.exsd"/>
+   <extension
+         point="org.eclipse.ui.views">
+      <view
+            class="org.eclipse.actf.ai.navigator.views.NavigatorTreeView"
+            icon="icons/treex16.gif"
+            id="org.eclipse.actf.ai.navigator.views.NavigatorTreeView"
+            name="%NavigationTreeView.name"/>
+   </extension>
+   <extension
+         point="org.eclipse.actf.model.ui.editors.ie.WebBrowserEventListener">
+      <listener
+            class="org.eclipse.actf.ai.navigator.impl.WebEventListener"
+            id="org.eclipse.actf.ai.navigator.impl.WebEventListener"/>
+   </extension>
+   <extension
+         point="org.eclipse.ui.preferencePages">
+      <page
+            class="org.eclipse.actf.ai.navigator.preferences.UserInfoPreferencePage"
+            id="org.eclipse.actf.ai.navigator.preferences.UserInfoPreferencePage"
+            name="%UserAnnotation"/>
+   </extension>
+   <extension
+         point="org.eclipse.core.runtime.preferences">
+      <initializer class="org.eclipse.actf.ai.navigator.preferences.UserInfoPreferenceInitializer"/>
+   </extension>
+   <extension
+         point="org.eclipse.actf.ai.navigator.MetadataCacheCleaner">
+      <cleaner class="org.eclipse.actf.ai.navigator.impl.NVM3EntryCacheCleaner"/>
+   </extension>
+
+</plugin>
diff --git a/plugins/org.eclipse.actf.ai.navigator/plugin_ja.properties b/plugins/org.eclipse.actf.ai.navigator/plugin_ja.properties
new file mode 100644
index 0000000..d861478
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/plugin_ja.properties
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 2007 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     Daisuke SATO - initial configuration.
+###############################################################################
+
+DeleteAction.name=\u30e1\u30bf\u30c7\u30fc\u30bf\u30ad\u30e3\u30c3\u30b7\u30e5\u3092\u524a\u9664
+DeleteAction.tip=\u30e1\u30bf\u30c7\u30fc\u30bf\u30ad\u30e3\u30c3\u30b7\u30e5\u3092\u524a\u9664\u3057\u307e\u3059
+
+NavigationTreeView.name=\u30ca\u30d3\u30b2\u30fc\u30b7\u30e7\u30f3\u30fb\u30c4\u30ea\u30fc\u30d3\u30e5\u30fc
+UserAnnotation=\u6ce8\u91c8
\ No newline at end of file
diff --git a/plugins/org.eclipse.actf.ai.navigator/schema/MediaControl.exsd b/plugins/org.eclipse.actf.ai.navigator/schema/MediaControl.exsd
new file mode 100644
index 0000000..7d11d9d
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/schema/MediaControl.exsd
@@ -0,0 +1,109 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.actf.ai.navigator">
+<annotation>
+      <appInfo>
+         <meta.schema plugin="org.eclipse.actf.ai.navigator" id="MediaControl" name="MediaControl"/>
+      </appInfo>
+      <documentation>
+         [Enter description of this extension point.]
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <complexType>
+         <sequence>
+            <element ref="controller"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute translatable="true"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="controller">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn="org.eclipse.actf.ai.navigator.IMediaControl"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="since"/>
+      </appInfo>
+      <documentation>
+         [Enter the first release in which this extension point appears.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="examples"/>
+      </appInfo>
+      <documentation>
+         [Enter extension point usage example here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="apiInfo"/>
+      </appInfo>
+      <documentation>
+         [Enter API information here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="implementation"/>
+      </appInfo>
+      <documentation>
+         [Enter information about supplied implementation of this extension point.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="copyright"/>
+      </appInfo>
+      <documentation>
+         Copyright (c) 2007 IBM Corporation and others.&lt;br&gt;
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Eclipse Public License v1.0 which accompanies
+this distribution, and is available at &lt;a 
+href=&quot;http://www.eclipse.org/legal/epl-v10.html&quot;&gt;http://www.eclipse.org/legal/epl-v10.html&lt;/a&gt;
+      </documentation>
+   </annotation>
+
+</schema>
diff --git a/plugins/org.eclipse.actf.ai.navigator/schema/MetadataCacheCleaner.exsd b/plugins/org.eclipse.actf.ai.navigator/schema/MetadataCacheCleaner.exsd
new file mode 100644
index 0000000..3914cac
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/schema/MetadataCacheCleaner.exsd
@@ -0,0 +1,109 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.actf.ai.navigator">
+<annotation>
+      <appInfo>
+         <meta.schema plugin="org.eclipse.actf.ai.navigator" id="MetadataCacheClear" name="MetadataCacheClear"/>
+      </appInfo>
+      <documentation>
+         [Enter description of this extension point.]
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <complexType>
+         <sequence>
+            <element ref="cleaner"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute translatable="true"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="cleaner">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn="org.eclipse.actf.ai.navigator.IMetadataCacheCleaner"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="since"/>
+      </appInfo>
+      <documentation>
+         [Enter the first release in which this extension point appears.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="examples"/>
+      </appInfo>
+      <documentation>
+         [Enter extension point usage example here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="apiInfo"/>
+      </appInfo>
+      <documentation>
+         [Enter API information here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="implementation"/>
+      </appInfo>
+      <documentation>
+         [Enter information about supplied implementation of this extension point.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="copyright"/>
+      </appInfo>
+      <documentation>
+         Copyright (c) 2007 IBM Corporation and others.&lt;br&gt;
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Eclipse Public License v1.0 which accompanies
+this distribution, and is available at &lt;a 
+href=&quot;http://www.eclipse.org/legal/epl-v10.html&quot;&gt;http://www.eclipse.org/legal/epl-v10.html&lt;/a&gt;
+      </documentation>
+   </annotation>
+
+</schema>
diff --git a/plugins/org.eclipse.actf.ai.navigator/schema/Navigation.exsd b/plugins/org.eclipse.actf.ai.navigator/schema/Navigation.exsd
new file mode 100644
index 0000000..9041d45
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/schema/Navigation.exsd
@@ -0,0 +1,109 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.actf.ai.navigator">
+<annotation>
+      <appInfo>
+         <meta.schema plugin="org.eclipse.actf.ai.navigator" id="Navigation" name="Navigation"/>
+      </appInfo>
+      <documentation>
+         [Enter description of this extension point.]
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <complexType>
+         <sequence>
+            <element ref="manipulator"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute translatable="true"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="manipulator">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn="org.eclipse.actf.ai.navigator.IManipulator"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="since"/>
+      </appInfo>
+      <documentation>
+         [Enter the first release in which this extension point appears.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="examples"/>
+      </appInfo>
+      <documentation>
+         [Enter extension point usage example here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="apiInfo"/>
+      </appInfo>
+      <documentation>
+         [Enter API information here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="implementation"/>
+      </appInfo>
+      <documentation>
+         [Enter information about supplied implementation of this extension point.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="copyright"/>
+      </appInfo>
+      <documentation>
+         Copyright (c) 2007 IBM Corporation and others.&lt;br&gt;
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Eclipse Public License v1.0 which accompanies
+this distribution, and is available at &lt;a 
+href=&quot;http://www.eclipse.org/legal/epl-v10.html&quot;&gt;http://www.eclipse.org/legal/epl-v10.html&lt;/a&gt;
+      </documentation>
+   </annotation>
+
+</schema>
diff --git a/plugins/org.eclipse.actf.ai.navigator/schema/ScreenReaderController.exsd b/plugins/org.eclipse.actf.ai.navigator/schema/ScreenReaderController.exsd
new file mode 100644
index 0000000..4e7894d
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/schema/ScreenReaderController.exsd
@@ -0,0 +1,109 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.actf.ai.navigator">
+<annotation>
+      <appInfo>
+         <meta.schema plugin="org.eclipse.actf.ai.navigator" id="ScreenReaderController" name="ScreenReaderController"/>
+      </appInfo>
+      <documentation>
+         [Enter description of this extension point.]
+      </documentation>
+   </annotation>
+
+   <element name="extension">
+      <complexType>
+         <sequence>
+            <element ref="controller"/>
+         </sequence>
+         <attribute name="point" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="id" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+            </annotation>
+         </attribute>
+         <attribute name="name" type="string">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute translatable="true"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <element name="controller">
+      <complexType>
+         <attribute name="class" type="string" use="required">
+            <annotation>
+               <documentation>
+                  
+               </documentation>
+               <appInfo>
+                  <meta.attribute kind="java" basedOn="org.eclipse.actf.ai.navigator.IScreenReaderControl"/>
+               </appInfo>
+            </annotation>
+         </attribute>
+      </complexType>
+   </element>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="since"/>
+      </appInfo>
+      <documentation>
+         [Enter the first release in which this extension point appears.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="examples"/>
+      </appInfo>
+      <documentation>
+         [Enter extension point usage example here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="apiInfo"/>
+      </appInfo>
+      <documentation>
+         [Enter API information here.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="implementation"/>
+      </appInfo>
+      <documentation>
+         [Enter information about supplied implementation of this extension point.]
+      </documentation>
+   </annotation>
+
+   <annotation>
+      <appInfo>
+         <meta.section type="copyright"/>
+      </appInfo>
+      <documentation>
+         Copyright (c) 2007 IBM Corporation and others.&lt;br&gt;
+All rights reserved. This program and the accompanying materials are made
+available under the terms of the Eclipse Public License v1.0 which accompanies
+this distribution, and is available at &lt;a 
+href=&quot;http://www.eclipse.org/legal/epl-v10.html&quot;&gt;http://www.eclipse.org/legal/epl-v10.html&lt;/a&gt;
+      </documentation>
+   </annotation>
+
+</schema>
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/messages.properties b/plugins/org.eclipse.actf.ai.navigator/src/messages.properties
new file mode 100644
index 0000000..de8d0fd
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/messages.properties
@@ -0,0 +1,37 @@
+###############################################################################
+# Copyright (c) 2007 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     Daisuke SATO - initial configuration.
+###############################################################################
+
+NavigatorTreeView.property=Property
+NavigatorTreeView.value=Value
+
+DialogOpenURL.Open_URL_2=Open a page
+
+IManipulator.TreeNavigaion=Tree Navigation
+IManipulator.FormInput=Form Input
+IManipulator.Input=Input
+FormInputDialog.Textarea=Text Area Input
+FormInputDialog.Password=Password Input
+FormInputDialog.Text=Text Input
+FormInputDialog.Search=Find Text
+FormInputDialog.Forward=&Forward
+FormInputDialog.Backward=&Backward
+FormInputDialog.Exact=&Case Sensitive
+
+FormSelectDialog.Single=Select an option
+FormSelectDialog.Multiple=Select options
+
+AccessKeyListDialog.Title=Access Key List
+
+AltInputDialog.Text=Alt Text Input
+
+UserInfo.PREFERENCES_NAME=User Annotation Preferences
+UserInfo.SAVE_ANNOTATION=&Save annotation automatically
+UserInfo.REFRESH_TREEVIEW=&Refresh Tree View automatically
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/messages_ja.properties b/plugins/org.eclipse.actf.ai.navigator/src/messages_ja.properties
new file mode 100644
index 0000000..0bc0958
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/messages_ja.properties
@@ -0,0 +1,39 @@
+###############################################################################
+# Copyright (c) 2007 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     Daisuke SATO - initial configuration.
+###############################################################################
+
+NavigatorTreeView.property=\u30d7\u30ed\u30d1\u30c6\u30a3
+NavigatorTreeView.value=\u5024
+
+DialogOpenURL.Open_URL_2=\u30da\u30fc\u30b8\u3092\u958b\u304f
+
+IManipulator.TreeNavigaion=\u30c4\u30ea\u30fc\u30ca\u30d3\u30b2\u30fc\u30b7\u30e7\u30f3
+IManipulator.FormInput=\u30d5\u30a9\u30fc\u30e0\u5165\u529b
+IManipulator.Input=\u5165\u529b
+
+FormInputDialog.Textarea=\u30c6\u30ad\u30b9\u30c8\u30a8\u30ea\u30a2\u306e\u5165\u529b
+FormInputDialog.Password=\u30d1\u30b9\u30ef\u30fc\u30c9\u306e\u5165\u529b
+FormInputDialog.Text=\u30c6\u30ad\u30b9\u30c8\u306e\u5165\u529b
+
+FormInputDialog.Search=\u6587\u5b57\u5217\u306e\u691c\u7d22
+FormInputDialog.Forward=\u4e0b\u3078(&F)
+FormInputDialog.Backward=\u4e0a\u3078(&B)
+FormInputDialog.Exact=\u5927\u6587\u5b57\u3068\u5c0f\u6587\u5b57\u3092\u533a\u5225\u3059\u308b(&C)
+
+FormSelectDialog.Single=\u9805\u76ee\u306e\u9078\u629e
+FormSelectDialog.Multiple=\u9805\u76ee\u306e\u8907\u6570\u9078\u629e
+
+AccessKeyListDialog.Title=\u30a2\u30af\u30bb\u30b9\u30ad\u30fc\u306e\u30ea\u30b9\u30c8
+
+AltInputDialog.Text=\u4ee3\u66ff\u30c6\u30ad\u30b9\u30c8\u306e\u5165\u529b
+
+UserInfo.PREFERENCES_NAME=\u6ce8\u91c8\u306e\u8a2d\u5b9a
+UserInfo.SAVE_ANNOTATION=\u81ea\u52d5\u7684\u306b\u6ce8\u91c8\u3092\u4fdd\u5b58\u3059\u308b(&S)
+UserInfo.REFRESH_TREEVIEW=\u81ea\u52d5\u7684\u306b\u30c4\u30ea\u30fc\u30d3\u30e5\u30fc\u3092\u66f4\u65b0\u3059\u308b(&R)
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IBrowserControl.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IBrowserControl.java
new file mode 100644
index 0000000..cd469fc
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IBrowserControl.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator;
+
+
+public interface IBrowserControl {
+    void forward();
+    void backward();
+    
+    void importMetadata();
+    void exportAllMetadata();
+
+    void openTab();
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IManipulator.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IManipulator.java
new file mode 100644
index 0000000..3f2eee6
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IManipulator.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator;
+
+
+public interface IManipulator {
+    public static class Mode {
+        public final String name;
+        public final int code;
+        private Mode(String name, int code) {
+            this.name = name;
+            this.code = code;
+        }
+    }
+    int TREE_NAVIGATION_MODE_CODE = 0;
+    Mode TREE_NAVIGATION_MODE = new Mode(Messages.getString("IManipulator.TreeNavigaion"), TREE_NAVIGATION_MODE_CODE); //$NON-NLS-1$
+    int FORM_INPUT_MODE_CODE = 1;
+    Mode FORM_INPUT_MODE = new Mode(Messages.getString("IManipulator.FormInput"), FORM_INPUT_MODE_CODE); //$NON-NLS-1$
+    int KEYHOOK_DISABLED_MODE_CODE = 2;
+    Mode KEYHOOK_DISABLED_MODE = new Mode(Messages.getString("IManipulator.Input"), KEYHOOK_DISABLED_MODE_CODE); //$NON-NLS-1$
+
+    void setBrowserControl(IBrowserControl browserControl);
+    void setNavigator(INavigatorUI navigatorUI);
+    void setMode(Mode mode);
+    void dispose();
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IMediaControl.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IMediaControl.java
new file mode 100644
index 0000000..7c3e0df
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IMediaControl.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator;
+
+import org.eclipse.actf.ai.fennec.treemanager.ISoundControl;
+import org.eclipse.actf.ai.fennec.treemanager.IVideoControl;
+import org.eclipse.actf.ai.voice.IVoice;
+import org.eclipse.actf.model.IWebBrowserACTF;
+
+
+
+
+public interface IMediaControl {
+
+    int TOGGLE_FAIL = 1;
+    
+    int STATUS_ON = 2;
+    
+    int STATUS_OFF = 4;
+    
+    int STATUS_NOT_AVAILABLE = 8;
+    
+    public interface IHandle {
+        IVideoControl getVideoControl();
+        ISoundControl getSoundControl();
+        IWebBrowserACTF getWebBrowser();
+        IVoice getVoice();
+    }
+    
+    void start(IHandle handle);
+    
+    void dispose(IHandle handle);
+
+    int toggleEnable();
+    
+    boolean isAvailable();
+    
+    boolean toggleViewShowing();
+
+    boolean isEnabled();
+
+    void speakInfo(boolean flush);
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IMetadataCacheCleaner.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IMetadataCacheCleaner.java
new file mode 100644
index 0000000..021e6fc
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IMetadataCacheCleaner.java
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator;
+
+
+public interface IMetadataCacheCleaner {
+    void clearCache();
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/INavigatorUI.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/INavigatorUI.java
new file mode 100644
index 0000000..454d3aa
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/INavigatorUI.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.navigator;
+
+public interface INavigatorUI {
+    // Tree Navigation Mode
+    void muteMedia();
+    void volumeDownMedia();
+    void volumeUpMedia();
+    
+    void minimalVolumeDownMedia();
+    void minimalVolumeUpMedia();
+    
+    void previousTrack();
+    void nextTrack();
+    void stopMedia();
+    void playMedia();
+    void pauseMedia();
+    void fastReverse();
+    void fastForward();
+    
+    void stopSpeak();
+
+    void speechSpeedUp();
+    void speechSpeedDown();
+    
+    void treeLeft();
+    void treeRight();
+    void treeUp();
+    void treeDown();
+
+    void treeTop();
+    void treeBottom();
+
+    void traverseNodeUp();
+    void traverseNodeDown();
+    void traverseDown();
+    void traverseUp();
+
+    void click();
+    void speakCurrentStatus();
+    void speakMediaStatus();
+
+    void nextHeader();
+    void previousHeader();
+
+    void nextInputable();
+    void previousInputable();
+    
+    void nextLink();
+    void previousLink();
+
+    void nextObject();
+    void previousObject();
+
+    void cellLeft();
+    void cellRight();
+    void cellUp();
+    void cellDown();
+
+    // Browser control
+    void navigateRefresh();
+    // void goForward();
+    // void goBackward();
+
+    // select NVM3;
+    void selectNextNVM3();
+
+    // Form Mode;
+    void exitFormMode();
+    void submitForm();
+
+    // browser address.
+    void enterBrowserAddress();
+
+    // tab operations.
+    void closeTab();
+
+    void searchNext();
+    void searchPrevious();
+    
+    void nextHeader1();
+    void previousHeader1();
+    void nextHeader2();
+    void previousHeader2();
+    void nextHeader3();
+    void previousHeader3();
+    void nextHeader4();
+    void previousHeader4();
+    void nextHeader5();
+    void previousHeader5();
+    void nextHeader6();
+    void previousHeader6();
+    
+    void nextListItem();
+    void previousListItem();
+    void nextBlock();
+    void previousBlock();
+
+    void nextMedia();
+    void previousMedia();
+    
+    void launchBrowser();
+    void toggleLeftViewsShowing();
+    void speakAll();
+    
+    void exportMetadata();
+    
+    void jumpToAccessKey(char key);
+    void showAccessKeyList();
+    
+    // force start.
+    void forceRestart();
+    void toggleDescriptionEnable();
+    void nextTab();
+    void prevTab();
+
+    // User Annotation Extension.
+    void nextAlterable();
+    void previousAlterable();
+    void editAltText();
+    void makeLandmark();
+    void saveUserInfo();
+    void removeUserInfo();
+    
+    // repairt
+    void repairFlash();
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IScreenReaderControl.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IScreenReaderControl.java
new file mode 100644
index 0000000..248a54e
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/IScreenReaderControl.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator;
+
+import org.eclipse.actf.model.IWebBrowserACTF;
+
+
+
+public interface IScreenReaderControl {
+    boolean isAvailable();
+
+    void screenReaderOn();
+    void screenReaderOff();
+    void takeBackControl(IWebBrowserACTF browser);
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/Messages.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/Messages.java
new file mode 100644
index 0000000..ea41664
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/Messages.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+
+
+public class Messages {
+    private static final String BUNDLE_NAME = "messages"; //$NON-NLS-1$
+
+    private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);
+
+    private Messages() {
+    }
+
+    public static String getString(String key) {
+        try {
+            return RESOURCE_BUNDLE.getString(key);
+        } catch (MissingResourceException e) {
+            return '!' + key + '!';
+        }
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/NavigatorPlugin.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/NavigatorPlugin.java
new file mode 100644
index 0000000..5e6d267
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/NavigatorPlugin.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.navigator;
+
+import org.eclipse.actf.ai.navigator.extension.MetadataCacheCleanerExtension;
+import org.eclipse.actf.ai.navigator.views.NavigatorTreeView;
+import org.eclipse.actf.ai.voice.internal.AbstractPreferenceUIPlugin;
+import org.eclipse.jface.action.IMenuManager;
+import org.osgi.framework.BundleContext;
+
+
+/**
+ * The main plugin class to be used in the desktop.
+ */
+public class NavigatorPlugin extends AbstractPreferenceUIPlugin {
+
+    public static final String ID = "org.eclipse.actf.ai.navigator";
+    //The shared instance.
+    private static NavigatorPlugin plugin;
+    public static IMenuManager menuManager;
+        
+    /**
+     * The constructor.
+     */
+    public NavigatorPlugin() {
+        plugin = this;
+    }
+
+    /**
+     * This method is called upon plug-in activation
+     */
+    @Override
+    public void start(BundleContext context) throws Exception {
+        super.start(context);
+    }
+
+    /**
+     * This method is called when the plug-in is stopped
+     */
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        super.stop(context);
+        plugin = null;
+    }
+
+    /**
+     * Returns the shared instance.
+     */
+    public static NavigatorPlugin getDefault() {
+        return plugin;
+    }
+
+    private INavigatorUI navigatorUI;
+
+    public void setNavigatorUI(INavigatorUI navigatorUI) {
+        this.navigatorUI = navigatorUI;
+    }
+    
+
+    public INavigatorUI getNavigatorUI() {
+        return navigatorUI;
+    }
+
+    private NavigatorTreeView navigatorTreeView;
+
+    public void setNavigatorTreeView(NavigatorTreeView navigatorTreeView) {
+        this.navigatorTreeView = navigatorTreeView;
+    }
+    
+    public NavigatorTreeView getNavigatorTreeView() {
+        return navigatorTreeView;
+    }
+    
+    public void clearMetadataCache(){
+        MetadataCacheCleanerExtension.clearCache();
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/actions/DeleteCacheAction.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/actions/DeleteCacheAction.java
new file mode 100644
index 0000000..191b430
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/actions/DeleteCacheAction.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.navigator.actions;
+
+import org.eclipse.actf.ai.navigator.extension.MetadataCacheCleanerExtension;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+
+public class DeleteCacheAction implements IWorkbenchWindowActionDelegate {
+    
+	private IWorkbenchWindow window;
+
+    public void init(IWorkbenchWindow window) {
+        this.window = window;
+    }
+
+    public void dispose() {
+        
+    }
+
+    public void run(IAction action) {
+        MetadataCacheCleanerExtension.clearCache();
+    }
+
+    public void selectionChanged(IAction action, ISelection selection) {
+        
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/broker/Mirror.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/broker/Mirror.java
new file mode 100644
index 0000000..02f6a84
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/broker/Mirror.java
@@ -0,0 +1,223 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.broker;
+
+
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class Mirror {
+    final private Class class1;
+    final private Object object;
+
+    public Object getObject() {
+        return object;
+    }
+
+    private Class[] parseMethodSignature(String params) {
+        String[] paramArray;
+
+        paramArray = params.split(", *");
+        Class[] cs = new Class[paramArray.length];
+        try {
+            for (int i = 0; i < paramArray.length; i++) {
+                if ("int".equals(paramArray[i])) {
+                    cs[i] = java.lang.Integer.TYPE;
+                } else if ("short".equals(paramArray[i])) {
+                    cs[i] = java.lang.Short.TYPE;
+                } else if ("long".equals(paramArray[i])) {
+                    cs[i] = java.lang.Long.TYPE;
+                } else if ("char".equals(paramArray[i])) {
+                    cs[i] = java.lang.Character.TYPE;
+                } else if ("boolean".equals(paramArray[i])) {
+                    cs[i] = java.lang.Boolean.TYPE;
+                } else if ("byte".equals(paramArray[i])) {
+                    cs[i] = java.lang.Byte.TYPE;
+                } else if ("float".equals(paramArray[i])) {
+                    cs[i] = java.lang.Float.TYPE;
+                } else if ("double".equals(paramArray[i])) {
+                    cs[i] = java.lang.Double.TYPE;
+                } else {
+                    cs[i] = Class.forName(paramArray[i], true, class1.getClassLoader());
+                }
+            }
+        } catch (ClassNotFoundException e) {
+            return null;
+        }
+
+        return cs;
+    }
+
+    public Method getMethod(String signature) {
+        String name;
+        Class[] params = null;
+        int posParam = signature.indexOf('(');
+        if (posParam == -1) {
+            name = signature;
+        } else {
+            name = signature.substring(0, posParam);
+            int posParamEnd = signature.lastIndexOf(')');
+            if (posParamEnd == -1) return null;
+            params = parseMethodSignature(signature.substring(posParam + 1, posParamEnd));
+            if (params == null) return null;
+        }
+
+        Method m = null;
+        for (Class c = class1; c != null; c = c.getSuperclass()) {
+            try {
+                if (params == null) {
+                    Method[] ms = c.getDeclaredMethods();
+                    for (int i = 0; i < ms.length; i++) {
+                        if (name.equals(ms[i].getName())) {
+                            m = ms[i];
+                        }
+                    }
+                } else {
+                    m = c.getDeclaredMethod(name, params);
+                }
+            } catch (SecurityException e) {
+                continue;
+            } catch (NoSuchMethodException e) {
+                continue;
+            }
+            if (m != null) {
+                m.setAccessible(true);
+                return m;
+            }
+        }
+
+        return null;
+    }
+
+    public Object invoke(String signature, Object[] params) throws Exception {
+        Method m = getMethod(signature);
+        return m.invoke(object, params);
+    }
+
+    public Field getField(String name) {
+        Field f;
+
+        for (Class c = class1; c != null; c = c.getSuperclass()) {
+            try {
+                f = c.getDeclaredField(name);
+            } catch (SecurityException e) {
+                continue;
+            } catch (NoSuchFieldException e) {
+                continue;
+            }
+            if (f != null) {
+                f.setAccessible(true);
+                return f;
+            }
+        }
+        return null;
+    }
+
+    public Object getFieldObject(String name) throws IllegalAccessException {
+        return getField(name).get(object);
+    }
+
+    static private boolean isParamAccept(Class cp, Class c) {
+        if (cp.isAssignableFrom(c)) return true;
+
+        if ((java.lang.Integer.TYPE == cp)
+            && java.lang.Integer.class.equals(c))
+            return true;
+        if ((java.lang.Short.TYPE == cp)
+            && java.lang.Short.class.equals(c))
+            return true;
+        if ((java.lang.Long.TYPE == cp)
+            && java.lang.Long.class.equals(c))
+            return true;
+        if ((java.lang.Character.TYPE == cp)
+            && java.lang.Character.class.equals(c))
+            return true;
+        if ((java.lang.Boolean.TYPE == cp)
+            && java.lang.Boolean.class.equals(c))
+            return true;
+        if ((java.lang.Byte.TYPE == cp)
+            && java.lang.Byte.class.equals(c))
+            return true;
+        if ((java.lang.Float.TYPE == cp)
+            && java.lang.Float.class.equals(c))
+        if ((java.lang.Double.TYPE == cp)
+            && java.lang.Double.class.equals(c))
+            return true;
+
+        return false;
+    }
+
+    public Constructor findConstructor(Object[] params) {
+        Constructor[] cs = class1.getDeclaredConstructors();
+
+        nextConstructor:
+        for (int i = 0; i < cs.length; i++) {
+            Class[] cParams = cs[i].getParameterTypes();
+            if (params == null) {
+                if (cParams.length == 0) return cs[i];
+                continue nextConstructor;
+            }
+            if (cParams.length != params.length) continue nextConstructor;;
+            for (int j = 0; j < params.length; j++) {
+                if (!isParamAccept(cParams[j], params[j].getClass())) continue nextConstructor;
+            }
+            return cs[i];
+        }
+
+        return null;
+    }
+
+    public Object newInstance(Object[] params)
+    	throws InvocationTargetException, IllegalAccessException, InstantiationException {
+        return findConstructor(params).newInstance(params);
+    }
+
+    public Mirror(String className) throws ClassNotFoundException {
+        this.class1 = Class.forName(className);
+        this.object = null;
+    }
+
+    public Mirror(Class cls) {
+        this.class1 = cls;
+        this.object = null;
+    }
+
+    public Mirror(Object object) {
+        this.class1 = object.getClass();
+        this.object = object;
+    }
+
+    public Mirror(String className, Object[] params)
+    	throws ClassNotFoundException,
+               InvocationTargetException, IllegalAccessException, InstantiationException {
+        this.class1 = Class.forName(className);
+        this.object = newInstance(params);
+    }
+
+    public static void main(String[] args) throws Exception {
+        Mirror miS = new Mirror("java.lang.String");
+
+        System.out.println("java.lang.String.charAt:"
+                           + miS.getMethod("charAt"));
+        System.out.println("java.lang.String.indexOf(int,int):"
+                           + miS.getMethod("indexOf(int, int)"));
+
+        Mirror miS2 = new Mirror("java.lang.String", new Object[]{"test string"});
+        System.out.println("String instanciation test:" + miS2.getObject());
+
+        Mirror miI = new Mirror("java.lang.Integer", new Object[]{new Integer(100)});
+        System.out.println("Integer instanciation test:" + miI.getObject());
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/broker/RequestBroker.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/broker/RequestBroker.java
new file mode 100644
index 0000000..c912322
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/broker/RequestBroker.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.navigator.broker;
+
+import java.lang.reflect.Method;
+
+import org.eclipse.actf.ai.fennec.treemanager.ITreeManager;
+import org.eclipse.actf.ai.navigator.impl.NavigatorImpl;
+import org.eclipse.actf.ai.navigator.impl.WebEventListener;
+import org.eclipse.actf.model.IWebBrowserACTF;
+import org.eclipse.actf.util.timer.Yield;
+
+
+public class RequestBroker {
+    public static final int SHOW_STATUS = 100;
+	
+    public static final int EVENT_NEW_URL = 101;
+
+    public static final int EVENT_NEWPAGE_READY = 1000;
+	
+    public static final int EVENT_LOAD_STARTING = 1001;
+	
+    public static final int EVENT_WAIT_FOR_PROCESSING = 1200;
+	
+    public static final int EVENT_INFORMATION_UPDATED = 1201;
+
+    public static final int EVENT_TREE_MODIFIED = 1202;
+
+    public static final int EVENT_NOTIFICATION = 1203;
+	
+    public static final int EVENT_ALERT_MODAL = 1204;
+	
+    public static final int EVENT_AUTOMATIC_TRANSITION = 1205;
+	
+    private final WebEventListener webEventListener;
+    private NavigatorImpl navigator;
+    private IWebBrowserACTF webBrowser;
+
+    private boolean enabled;
+
+    private Method registerRequestBrokerMethod;
+    private Method sendEventMethod;
+
+    public void setNavigator(NavigatorImpl navigator, IWebBrowserACTF webBrowser) {
+        this.navigator = navigator;
+        this.webBrowser = webBrowser;
+    }
+
+    private void registerProxy() {
+        if (!enabled) return;
+        try {
+            Object o = Thread.currentThread();
+            registerRequestBrokerMethod.invoke(o, new Object[] { this });
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void sendEvent(int id, Object param) {
+        if (!enabled) return;
+        System.err.println("EventRP:" + id + " Param:" + param);
+        try {
+            Object o = Thread.currentThread();
+            sendEventMethod.invoke(o, new Object[] { new Integer(id), param });
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void newPageReady() {
+        sendEvent(EVENT_NEW_URL, webBrowser.getURL());
+        sendEvent(EVENT_NEWPAGE_READY, null);
+    }
+
+    public Object invokeNavigator(String signature, Object[] args) throws Exception {
+        Mirror m = new Mirror(navigator);
+        Method method = m.getMethod(signature);
+        return Yield.syncInvoke(method, navigator, args);
+    }
+
+    public Object invokeTreeManager(String signature, Object[] args) throws Exception {
+        ITreeManager treeManager = navigator.getTreeManager();
+        Mirror m = new Mirror(treeManager);
+        Method method = m.getMethod(signature);
+        return Yield.syncInvoke(method, treeManager, args);
+    }
+
+    public Object invokeITreeItem(Object item, String signature, Object[] args) throws Exception {
+        Mirror m = new Mirror(item);
+        Method method = m.getMethod(signature);
+        return Yield.syncInvoke(method, item, args);
+    }
+
+    private void initialize() {
+        Object o = Thread.currentThread();
+        Class c = o.getClass();
+        try {
+            registerRequestBrokerMethod = c.getMethod("registerRequestBroker", new Class[] { Object.class });
+            sendEventMethod = c.getMethod("sendEvent", new Class[] { java.lang.Integer.TYPE, Object.class });
+            enabled = true;
+        } catch (NoSuchMethodException e) {
+            enabled = false;
+        }
+    }
+
+    public RequestBroker(WebEventListener webEventListener) {
+    	this.webEventListener = webEventListener;
+        initialize();
+        registerProxy();
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/extension/ManipulatorExtension.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/extension/ManipulatorExtension.java
new file mode 100644
index 0000000..93fad37
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/extension/ManipulatorExtension.java
@@ -0,0 +1,117 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.extension;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.actf.ai.navigator.IBrowserControl;
+import org.eclipse.actf.ai.navigator.IManipulator;
+import org.eclipse.actf.ai.navigator.INavigatorUI;
+import org.eclipse.actf.ai.navigator.NavigatorPlugin;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.Platform;
+
+
+
+
+public class ManipulatorExtension {
+    private static final String TAG_MANIPULATOR = "manipulator";
+    private static final String ATTR_CLASS = "class";
+
+    private static ManipulatorExtension[] cachedExtensions;
+
+    public static ManipulatorExtension[] getExtensions() {
+        if (cachedExtensions != null) return cachedExtensions;
+
+        IExtension[] extensions = Platform.getExtensionRegistry()
+            .getExtensionPoint(NavigatorPlugin.ID, "Navigation")
+            .getExtensions();
+        List<ManipulatorExtension> l = new ArrayList<ManipulatorExtension>();
+        for (int i = 0; i < extensions.length; i++) {
+            IConfigurationElement[] configElements =
+                extensions[i].getConfigurationElements();
+            for (int j = 0; j < configElements.length; j++) {
+                ManipulatorExtension ex = parseExtension(configElements[j], l.size());
+                if (ex != null) l.add(ex);
+            }
+        }
+        cachedExtensions = l.toArray(new ManipulatorExtension[l.size()]);
+        return cachedExtensions;
+    }
+
+    private static ManipulatorExtension parseExtension(IConfigurationElement configElement, int idx) {
+        if (!configElement.getName().equals(TAG_MANIPULATOR))
+            return null;
+        try {
+            return new ManipulatorExtension(configElement);
+        } catch (Exception e) {
+        }
+        return null;
+    }
+
+    public static void disposeExtensions() {
+        if (cachedExtensions == null) return;
+        for (int i = 0; i < cachedExtensions.length; i++) {
+            cachedExtensions[i].dispose();
+        }
+        cachedExtensions = null;
+    }
+
+    public static void setBrowserControl(IBrowserControl browserControl) {
+        ManipulatorExtension[] exs = getExtensions();
+        if (exs == null) return;
+        for (int i = 0; i < exs.length; i++) {
+            exs[i].getManipulator().setBrowserControl(browserControl);
+        }
+    }
+
+    public static void setNavigator(INavigatorUI navigatorUI) {
+        ManipulatorExtension[] exs = getExtensions();
+        if (exs == null) return;
+        for (int i = 0; i < exs.length; i++) {
+            exs[i].getManipulator().setNavigator(navigatorUI);
+        }
+    }
+
+    public static void setMode(IManipulator.Mode mode) {
+        ManipulatorExtension[] exs = getExtensions();
+        if (exs == null) return;
+        for (int i = 0; i < exs.length; i++) {
+            exs[i].getManipulator().setMode(mode);
+        }
+    }
+
+    private final IConfigurationElement configElement;
+    private IManipulator manipulator;
+
+    private ManipulatorExtension(IConfigurationElement configElement) {
+        this.configElement = configElement;
+    }
+
+    private IManipulator getManipulator() {
+        if (manipulator != null) return manipulator;
+        try {
+            manipulator = (IManipulator) configElement.createExecutableExtension(ATTR_CLASS);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return manipulator;
+    }
+
+    private void dispose() {
+        if (manipulator == null) return;
+        manipulator.dispose();
+        manipulator = null;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/extension/MediaControlExtension.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/extension/MediaControlExtension.java
new file mode 100644
index 0000000..75869b3
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/extension/MediaControlExtension.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.extension;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.actf.ai.navigator.IMediaControl;
+import org.eclipse.actf.ai.navigator.NavigatorPlugin;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.Platform;
+
+
+
+
+public class MediaControlExtension {
+    private static final String TAG_CONTROLLER = "controller";
+
+    private static final String ATTR_CLASS = "class";
+
+    private static MediaControlExtension[] cachedExtensions;
+
+    public static MediaControlExtension[] getExtensions() {
+        if (cachedExtensions != null)
+            return cachedExtensions;
+
+        IExtension[] extensions = Platform.getExtensionRegistry().getExtensionPoint(NavigatorPlugin.ID, "MediaControl")
+                .getExtensions();
+        List<MediaControlExtension> l = new ArrayList<MediaControlExtension>();
+        for (int i = 0; i < extensions.length; i++) {
+            IConfigurationElement[] configElements = extensions[i].getConfigurationElements();
+            for (int j = 0; j < configElements.length; j++) {
+                MediaControlExtension ex = parseExtension(configElements[j], l.size());
+                if (ex != null)
+                    l.add(ex);
+            }
+        }
+        cachedExtensions = l.toArray(new MediaControlExtension[l.size()]);
+        return cachedExtensions;
+    }
+
+    private static MediaControlExtension parseExtension(IConfigurationElement configElement, int idx) {
+        if (!configElement.getName().equals(TAG_CONTROLLER))
+            return null;
+        try {
+            return new MediaControlExtension(configElement);
+        } catch (Exception e) {
+        }
+        return null;
+    }
+
+    public static void disposeExtensions() {
+        if (cachedExtensions == null)
+            return;
+        for (int i = 0; i < cachedExtensions.length; i++) {
+            cachedExtensions[i].dispose();
+        }
+        cachedExtensions = null;
+    }
+
+    public static void start(IMediaControl.IHandle handle) {
+        MediaControlExtension[] exs = getExtensions();
+        if (exs == null)
+            return;
+        for (int i = 0; i < exs.length; i++) {
+            exs[i].getMediaControl().start(handle);
+        }
+    }
+
+    public static void doDispose(IMediaControl.IHandle handle) {
+        MediaControlExtension[] exs = getExtensions();
+        if (exs == null)
+            return;
+        for (int i = 0; i < exs.length; i++) {
+            exs[i].getMediaControl().dispose(handle);
+        }
+    }
+
+    private final IConfigurationElement configElement;
+
+    private IMediaControl controller;
+
+    private MediaControlExtension(IConfigurationElement configElement) {
+        this.configElement = configElement;
+    }
+
+    private IMediaControl getMediaControl() {
+        if (controller != null)
+            return controller;
+        try {
+            controller = (IMediaControl) configElement.createExecutableExtension(ATTR_CLASS);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return controller;
+    }
+
+    private void dispose() {
+        if (controller == null)
+            return;
+        controller = null;
+    }
+
+    public static int toggleEnable() {
+        MediaControlExtension[] exs = getExtensions();
+        int result = 0;
+        if (exs == null)
+            return IMediaControl.TOGGLE_FAIL;
+        for (int i = 0; i < exs.length; i++) {
+            result |= exs[i].getMediaControl().toggleEnable();
+        }
+        return result;
+    }
+    
+    public static boolean isAvailable(){
+        MediaControlExtension[] exs = getExtensions();
+        if (exs == null)
+            return false;
+        
+        boolean ret = false;
+        for (int i = 0; i < exs.length; i++) {
+            ret |= exs[i].getMediaControl().isAvailable();
+        }
+        return ret;
+    }
+
+    public static boolean toggleViewShowing() {
+        MediaControlExtension[] exs = getExtensions();
+        boolean result = true;
+        for (int i = 0; i < exs.length; i++) {
+            result = exs[i].getMediaControl().toggleViewShowing() && result;
+        }
+        return result;
+    }
+
+    public static boolean isEnabled() {
+        MediaControlExtension[] exs = getExtensions();
+        if (exs == null)
+            return false;
+        
+        boolean ret = false;
+        for (int i = 0; i < exs.length; i++) {
+            ret |= exs[i].getMediaControl().isEnabled();
+        }
+        return ret;
+    }
+
+    public static void speakInfo(boolean flush) {
+        MediaControlExtension[] exs = getExtensions();
+        if (exs == null)
+            return;
+
+        for (int i = 0; i < exs.length; i++) {
+            exs[i].getMediaControl().speakInfo(flush);
+        }
+    }
+
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/extension/MetadataCacheCleanerExtension.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/extension/MetadataCacheCleanerExtension.java
new file mode 100644
index 0000000..c0f35e0
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/extension/MetadataCacheCleanerExtension.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.extension;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.actf.ai.navigator.IMetadataCacheCleaner;
+import org.eclipse.actf.ai.navigator.NavigatorPlugin;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.Platform;
+
+
+
+
+public class MetadataCacheCleanerExtension {
+    private static final String TAG_CLEANER = "cleaner";
+    private static final String ATTR_CLASS = "class";
+
+    private static MetadataCacheCleanerExtension[] cachedExtensions;
+
+    public static MetadataCacheCleanerExtension[] getExtensions() {
+        if (cachedExtensions != null) return cachedExtensions;
+
+        IExtension[] extensions = Platform.getExtensionRegistry()
+            .getExtensionPoint(NavigatorPlugin.ID, "MetadataCacheCleaner")
+            .getExtensions();
+        List<MetadataCacheCleanerExtension> l = new ArrayList<MetadataCacheCleanerExtension>();
+        for (int i = 0; i < extensions.length; i++) {
+            IConfigurationElement[] configElements =
+                extensions[i].getConfigurationElements();
+            for (int j = 0; j < configElements.length; j++) {
+                MetadataCacheCleanerExtension ex = parseExtension(configElements[j], l.size());
+                if (ex != null) l.add(ex);
+            }
+        }
+        cachedExtensions = l.toArray(new MetadataCacheCleanerExtension[l.size()]);
+        return cachedExtensions;
+    }
+
+    private static MetadataCacheCleanerExtension parseExtension(IConfigurationElement configElement, int idx) {
+        if (!configElement.getName().equals(TAG_CLEANER))
+            return null;
+        try {
+            return new MetadataCacheCleanerExtension(configElement);
+        } catch (Exception e) {
+        }
+        return null;
+    }
+
+    public static void disposeExtensions() {
+        if (cachedExtensions == null) return;
+        for (int i = 0; i < cachedExtensions.length; i++) {
+            cachedExtensions[i].dispose();
+        }
+        cachedExtensions = null;
+    }
+    
+    public static void clearCache(){
+        MetadataCacheCleanerExtension[] exs = getExtensions();
+        if (exs == null) return;
+        for (int i = 0; i < exs.length; i++) {
+            exs[i].getMetadataCacheCleaner().clearCache();
+        }
+    }
+
+    private final IConfigurationElement configElement;
+    private IMetadataCacheCleaner cleaner;
+
+    private MetadataCacheCleanerExtension(IConfigurationElement configElement) {
+        this.configElement = configElement;
+    }
+
+    private IMetadataCacheCleaner getMetadataCacheCleaner() {
+        if (cleaner != null) return cleaner;
+        try {
+            cleaner = (IMetadataCacheCleaner) configElement.createExecutableExtension(ATTR_CLASS);
+        } catch (Exception e) {
+        }
+        return cleaner;
+    }
+
+    private void dispose() {
+        if (cleaner == null) return;
+        cleaner = null;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/extension/ScreenReaderExtension.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/extension/ScreenReaderExtension.java
new file mode 100644
index 0000000..1c71c4f
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/extension/ScreenReaderExtension.java
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.extension;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.actf.ai.navigator.IScreenReaderControl;
+import org.eclipse.actf.ai.navigator.NavigatorPlugin;
+import org.eclipse.actf.model.IWebBrowserACTF;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.Platform;
+
+
+
+
+public class ScreenReaderExtension {
+    private static final String TAG_CONTROLLER = "controller";
+    private static final String ATTR_CLASS = "class";
+
+    private static ScreenReaderExtension[] cachedExtensions;
+
+    public static ScreenReaderExtension[] getExtensions() {
+        if (cachedExtensions != null) return cachedExtensions;
+
+        IExtension[] extensions = Platform.getExtensionRegistry()
+            .getExtensionPoint(NavigatorPlugin.ID, "ScreenReaderController")
+            .getExtensions();
+        List<ScreenReaderExtension> l = new ArrayList<ScreenReaderExtension>();
+        for (int i = 0; i < extensions.length; i++) {
+            IConfigurationElement[] configElements =
+                extensions[i].getConfigurationElements();
+            for (int j = 0; j < configElements.length; j++) {
+                ScreenReaderExtension ex = parseExtension(configElements[j], l.size());
+                if (ex != null) l.add(ex);
+            }
+        }
+        cachedExtensions = l.toArray(new ScreenReaderExtension[l.size()]);
+        return cachedExtensions;
+    }
+
+    private static ScreenReaderExtension parseExtension(IConfigurationElement configElement, int idx) {
+        if (!configElement.getName().equals(TAG_CONTROLLER))
+            return null;
+        try {
+            return new ScreenReaderExtension(configElement);
+        } catch (Exception e) {
+        }
+        return null;
+    }
+
+    public static void disposeExtensions() {
+        if (cachedExtensions == null) return;
+        for (int i = 0; i < cachedExtensions.length; i++) {
+            cachedExtensions[i].dispose();
+        }
+        cachedExtensions = null;
+    }
+
+    public static void screenReaderOn() {
+        ScreenReaderExtension[] exs = getExtensions();
+        if (exs == null) return;
+        for (int i = 0; i < exs.length; i++) {
+            exs[i].getScreenReaderControl().screenReaderOn();
+        }
+    }
+
+    public static void screenReaderOff() {
+        ScreenReaderExtension[] exs = getExtensions();
+        if (exs == null) return;
+        for (int i = 0; i < exs.length; i++) {
+            exs[i].getScreenReaderControl().screenReaderOff();
+        }
+    }
+
+    public static boolean isAvailable() {
+        ScreenReaderExtension[] exs = getExtensions();
+        if (exs == null) return false;
+        boolean result = false;
+        for (int i = 0; i < exs.length; i++) {
+            result |= exs[i].getScreenReaderControl().isAvailable();
+        }
+        return result;
+    }
+    
+    public static void takeBackControl(IWebBrowserACTF browser) {
+        ScreenReaderExtension[] exs = getExtensions();
+        if (exs == null) return;
+        for (int i = 0; i < exs.length; i++) {
+            exs[i].getScreenReaderControl().takeBackControl(browser);
+        }
+    }
+
+    private final IConfigurationElement configElement;
+    private IScreenReaderControl controller;
+
+    private ScreenReaderExtension(IConfigurationElement configElement) {
+        this.configElement = configElement;
+    }
+
+    private IScreenReaderControl getScreenReaderControl() {
+        if (controller != null) return controller;
+        try {
+            controller = (IScreenReaderControl) configElement.createExecutableExtension(ATTR_CLASS);
+        } catch (Exception e) {
+        }
+        return controller;
+    }
+
+    private void dispose() {
+        if (controller == null) return;
+        controller = null;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/AccessKeyListDialog.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/AccessKeyListDialog.java
new file mode 100644
index 0000000..f2ba059
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/AccessKeyListDialog.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.impl;
+
+import org.eclipse.actf.ai.fennec.treemanager.IAccessKeyList;
+import org.eclipse.actf.ai.navigator.Messages;
+import org.eclipse.jface.dialogs.Dialog;
+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.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Shell;
+
+
+
+
+public class AccessKeyListDialog extends Dialog {
+    IAccessKeyList accessKeyList;
+
+    private char selectedKey = 0;
+
+    private String[] names;
+
+    List list;
+
+    @Override
+    protected void configureShell(Shell newShell) {
+        super.configureShell(newShell);
+        
+        newShell.setText(Messages.getString("AccessKeyListDialog.Title")); //$NON-NLS-1$
+
+    }
+
+    @Override
+    protected Control createDialogArea(Composite parent) {
+        Composite container = (Composite) super.createDialogArea(parent);
+        GridData gd;
+        gd = new GridData(GridData.FILL_BOTH);
+        gd.widthHint = 200;
+        gd.heightHint = 200;
+        gd.horizontalSpan = ((GridLayout) parent.getLayout()).numColumns;
+
+        list = new List(container, SWT.V_SCROLL | SWT.BORDER);
+
+        list.setLayoutData(gd);
+
+        for (int i = 0; i < accessKeyList.size(); i++) {
+            char key = accessKeyList.getAccessKeyAt(i);
+            String str = accessKeyList.getUIStringAt(i);
+            list.add(key+" "+str);
+        }
+
+        names = new String[list.getItemCount()];
+        for (int i = 0; i < names.length; i++)
+            names[i] = list.getItem(i);
+
+        list.addSelectionListener(new SelectionListener() {
+            public void widgetSelected(SelectionEvent e) {
+                selectedKey = accessKeyList.getAccessKeyAt(list.getSelectionIndex());
+            }
+
+            public void widgetDefaultSelected(SelectionEvent e) {
+                selectedKey = accessKeyList.getAccessKeyAt(list.getSelectionIndex());
+            }
+        });
+        return container;
+    }
+
+    AccessKeyListDialog(Shell parent, IAccessKeyList list) {
+        super(parent);
+        this.accessKeyList = list;
+    }
+
+    public char getSelectedKey() {
+        return selectedKey;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/BrowserControlImpl.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/BrowserControlImpl.java
new file mode 100644
index 0000000..b703440
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/BrowserControlImpl.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.impl;
+
+import java.io.File;
+
+import org.eclipse.actf.ai.navigator.IBrowserControl;
+import org.eclipse.actf.ai.navigator.IManipulator;
+import org.eclipse.actf.ai.navigator.extension.ManipulatorExtension;
+import org.eclipse.actf.ai.navigator.ui.URLOpenDialog;
+import org.eclipse.actf.ai.navigator.voice.VoiceManager;
+import org.eclipse.actf.ai.xmlstore.XMLStorePlugin;
+import org.eclipse.actf.model.ModelServiceUtils;
+import org.eclipse.actf.model.IWebBrowserACTF;
+import org.eclipse.actf.model.ui.editor.events.IWebBrowserNavigationEventListener;
+import org.eclipse.actf.model.ui.editor.events.WebBrowserNavigationEvent;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+
+
+
+public class BrowserControlImpl implements IBrowserControl, IWebBrowserNavigationEventListener {
+    private final TripJournal tripJournal;
+
+    private final WebEventListener webEventListener;
+
+    private final VoiceManager voiceManager = new VoiceManager(null);
+
+    public void backward() {
+        tripJournal.backward(webEventListener.getFocused());
+    }
+
+    public void forward() {
+        tripJournal.forward(webEventListener.getFocused());
+    }
+
+    BrowserControlImpl(WebEventListener webEventListener, TripJournal tripJournal) {
+        this.tripJournal = tripJournal;
+        this.webEventListener = webEventListener;
+    }
+
+    public void goBack(WebBrowserNavigationEvent e) {
+        tripJournal.backward(webEventListener.getFocused());
+    }
+
+    public void goForward(WebBrowserNavigationEvent e) {
+        tripJournal.forward(webEventListener.getFocused());
+    }
+
+    public void refresh(WebBrowserNavigationEvent e) {
+        IWebBrowserACTF wb = e.getBrowser();
+        WebEventListener.BrowserState bs = webEventListener.getBrowserState(wb);
+        if (bs == null)
+            return;
+        bs.getNavigator().navigateRefresh();
+    }
+
+    public void stop(WebBrowserNavigationEvent e) {
+        IWebBrowserACTF wb = e.getBrowser();
+        wb.navigateStop();
+    }
+
+    public void exportAllMetadata() {
+        String[] ext = { "*.fnz" };
+
+        voiceManager.speakWithFormat("Navigator.EXPORT_ALL_ANNOTATIONS", true, true);
+        Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+        FileDialog fileDialog = new FileDialog(shell, SWT.OPEN);
+        fileDialog.setFilterExtensions(ext);
+        String path = fileDialog.open();
+
+        if (path != null) {
+            if (!path.endsWith(".fnz")) {
+                path = path + ".fnz";
+            }
+            File dest = new File(path);
+            if (dest.exists()) {
+                String title = voiceManager.getMessageFormatter().mes("Navigator.OVERWRITE_CONFIRM");
+                String message = voiceManager.getMessageFormatter().mes("Navigator.OVERWRITE_MESSAGE", dest.getName());
+                boolean ret = MessageDialog.openQuestion(shell, title, message);
+                if (!ret)
+                    return;
+            }
+            if (XMLStorePlugin.getDefault().getXMLStoreService().exportAllAnnotations(dest)) {
+                voiceManager.speakWithFormat("Navigator.EXPORT_IS_SUCCEEDED", true, true);
+            } else {
+                voiceManager.speakWithFormat("Navigator.EXPORT_IS_FAILED", true, true);
+            }
+        }
+    }
+
+    public void importMetadata() {
+        String[] ext = { "*.fnc;*.fnz;*.xml" };
+
+        voiceManager.speakWithFormat("Navigator.IMPORT_FENNEC_FILE", true, true);
+        Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+        FileDialog fileDialog = new FileDialog(shell, SWT.OPEN);
+        fileDialog.setFilterExtensions(ext);
+        String path = fileDialog.open();
+
+        if (path != null) {
+            File src = new File(path);
+
+            boolean r = XMLStorePlugin.getDefault().getXMLStoreService().importMetadata(src);
+            XMLStorePlugin.getDefault().getXMLStoreService().getRootStore().refleshAll();
+            NavigatorImpl navigator = webEventListener.getFocused();
+            if (navigator != null)
+                navigator.forceRestart();
+            if (r) {
+                voiceManager.speakWithFormat("Navigator.IMPORT_IS_SUCCEEDED", true, true);
+            } else {
+                voiceManager.speakWithFormat("Navigator.IMPORT_IS_FAILED", true, true);
+            }
+        }
+    }
+
+
+    public void openTab() {
+        voiceManager.stop();
+        
+        URLOpenDialog openURLDialog = new URLOpenDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
+        if (1 == openURLDialog.open()) {
+            ManipulatorExtension.setMode(IManipulator.FORM_INPUT_MODE);
+            String sUrl = openURLDialog.getUrl();
+            ModelServiceUtils.openInExistingEditor(sUrl);
+            ManipulatorExtension.setMode(IManipulator.TREE_NAVIGATION_MODE);
+        }
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/FormInputDialog.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/FormInputDialog.java
new file mode 100644
index 0000000..55956d7
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/FormInputDialog.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.impl;
+
+import org.eclipse.actf.ai.navigator.Messages;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+
+
+
+public class FormInputDialog extends Dialog {
+    private String formInputString;
+
+    private Text formInputField;
+
+    private boolean multi;
+
+    private boolean pass;
+
+    @Override
+    protected void configureShell(Shell newShell) {
+        super.configureShell(newShell);
+        
+        if(multi)
+            newShell.setText(Messages.getString("FormInputDialog.Textarea")); //$NON-NLS-1$
+        else if(pass)
+            newShell.setText(Messages.getString("FormInputDialog.Password")); //$NON-NLS-1$
+        else
+            newShell.setText(Messages.getString("FormInputDialog.Text")); //$NON-NLS-1$
+    }
+
+    @Override
+    protected Control createDialogArea(Composite parent) {
+        Composite container = (Composite) super.createDialogArea(parent);
+        GridData gd;
+        if (multi) {
+            formInputField = new Text(container, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL);
+            gd = new GridData(GridData.FILL_BOTH);
+            gd.widthHint = 400;
+            gd.heightHint = 200;
+
+            formInputField.addKeyListener(new KeyListener(){
+                public void keyPressed(KeyEvent e) {
+                    if (e.keyCode == java.awt.event.KeyEvent.VK_TAB) {
+                       formInputField.traverse(SWT.TRAVERSE_TAB_NEXT);
+                       e.doit = false;
+                    }
+                }
+                public void keyReleased(KeyEvent e) {
+                }
+            });
+        } else {
+            if (pass)
+                formInputField = new Text(container, SWT.BORDER | SWT.PASSWORD);
+            else
+                formInputField = new Text(container, SWT.BORDER);
+            gd = new GridData(GridData.FILL_HORIZONTAL);
+            gd.widthHint = 400;
+        }
+        gd.horizontalSpan = ((GridLayout) parent.getLayout()).numColumns;
+        formInputField.setLayoutData(gd);
+        formInputField.setText(formInputString);
+
+        formInputField.addModifyListener(new ModifyListener() {
+            public void modifyText(ModifyEvent e) {
+                formInputString = formInputField.getText();
+            }
+        });
+        return container;
+    }
+
+    String getResult() {
+        return formInputString;
+    }
+
+    FormInputDialog(Shell parent, String init, boolean multi, boolean pass){
+        super(parent);
+        this.formInputString = init;
+        this.multi = multi;
+        this.pass = pass;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/FormSelectDialog.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/FormSelectDialog.java
new file mode 100644
index 0000000..7790e6c
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/FormSelectDialog.java
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.impl;
+
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.navigator.Messages;
+import org.eclipse.jface.dialogs.Dialog;
+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.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Shell;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+
+
+
+public class FormSelectDialog extends Dialog {
+    ITreeItem selectItem;
+
+    private int[] selectedIndices;
+    
+    private String[] names;
+    
+    boolean multiple;
+
+    List list;
+
+    @Override
+    protected void configureShell(Shell newShell) {
+        super.configureShell(newShell);
+
+        if (multiple) 
+            newShell.setText(Messages.getString("FormSelectDialog.Multiple")); //$NON-NLS-1$
+        else
+            newShell.setText(Messages.getString("FormSelectDialog.Single")); //$NON-NLS-1$
+    }
+
+    @Override
+    protected Control createDialogArea(Composite parent) {
+        Composite container = (Composite) super.createDialogArea(parent);
+        GridData gd;
+        gd = new GridData(GridData.FILL_BOTH);
+        gd.widthHint = 200;
+        gd.heightHint = 200;
+        gd.horizontalSpan = ((GridLayout) parent.getLayout()).numColumns;
+
+        if (multiple) 
+            list = new List(container, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
+        else  
+            list = new List(container, SWT.V_SCROLL | SWT.BORDER);
+
+        list.setLayoutData(gd);
+
+        Object base = selectItem.getBaseNode();
+        if (base instanceof Node) {
+            Node node = (Node) base;
+            NodeList nl = node.getChildNodes();
+            
+            for (int i = 0; i < nl.getLength(); i++) {
+                Node child = nl.item(i);
+                
+                if ("OPTION".equals(child.getNodeName())) {
+                    Node c = child.getFirstChild();
+                    String text = "";
+                    if (c != null)
+                        text = c.getNodeValue();
+                    list.add(text);
+                }
+            }
+        }
+        
+        names = new String[list.getItemCount()];
+        for (int i = 0; i < names.length; i++)
+            names[i] = list.getItem(i);
+
+        if (selectedIndices != null) {
+            list.select(selectedIndices);
+        }
+        
+        list.addSelectionListener(new SelectionListener(){
+            public void widgetSelected(SelectionEvent e) {
+                selectedIndices = list.getSelectionIndices();
+            }
+            public void widgetDefaultSelected(SelectionEvent e) {
+                
+                selectedIndices = list.getSelectionIndices();
+            }
+        });
+        return container;
+    }
+
+    FormSelectDialog(Shell parent, ITreeItem item, boolean multi) {
+        super(parent);
+        this.selectItem = item;
+        this.multiple = multi;
+        this.selectedIndices = item.getSelectedIndices();
+    }
+
+    public int[] getSelectedIndices() {
+        return selectedIndices;
+    }
+    
+    public String getTextAt(int index) {
+        if (0 <= index && index < names.length)
+            return names[index];
+        return "";
+    }
+
+    public int getLength() {
+        return names.length;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/NVM3EntryCacheCleaner.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/NVM3EntryCacheCleaner.java
new file mode 100644
index 0000000..ec2ac22
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/NVM3EntryCacheCleaner.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.impl;
+
+import org.eclipse.actf.ai.navigator.IMetadataCacheCleaner;
+
+
+
+public class NVM3EntryCacheCleaner implements IMetadataCacheCleaner {
+
+    public void clearCache() {
+        //NVM3Plugin.getDefault().initNVM3Entry();
+    }
+
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/NavigatorImpl.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/NavigatorImpl.java
new file mode 100644
index 0000000..f225ab1
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/NavigatorImpl.java
@@ -0,0 +1,2227 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.navigator.impl;
+
+import java.awt.Toolkit;
+import java.io.File;
+import java.net.URL;
+
+import org.eclipse.actf.ai.audio.io.AudioFactory;
+import org.eclipse.actf.ai.audio.io.IAudioPipe;
+import org.eclipse.actf.ai.audio.io.IAudioReader;
+import org.eclipse.actf.ai.audio.io.IAudioWriter;
+import org.eclipse.actf.ai.fennec.INVM3Entry;
+import org.eclipse.actf.ai.fennec.INVM3Mediator;
+import org.eclipse.actf.ai.fennec.treemanager.IAccessKeyList;
+import org.eclipse.actf.ai.fennec.treemanager.ILocation;
+import org.eclipse.actf.ai.fennec.treemanager.IMediaSyncEventListener;
+import org.eclipse.actf.ai.fennec.treemanager.ISoundControl;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.fennec.treemanager.ITreeManager;
+import org.eclipse.actf.ai.fennec.treemanager.IVideoControl;
+import org.eclipse.actf.ai.fennec.treemanager.TreeManagerException;
+import org.eclipse.actf.ai.fennec.treemanager.TreeManagerInterruptedException;
+import org.eclipse.actf.ai.fennec.treemanager.ISoundControl.VolumeState;
+import org.eclipse.actf.ai.fennec.treemanager.IVideoControl.VideoState;
+import org.eclipse.actf.ai.navigator.IManipulator;
+import org.eclipse.actf.ai.navigator.IMediaControl;
+import org.eclipse.actf.ai.navigator.INavigatorUI;
+import org.eclipse.actf.ai.navigator.NavigatorPlugin;
+import org.eclipse.actf.ai.navigator.IMediaControl.IHandle;
+import org.eclipse.actf.ai.navigator.extension.ManipulatorExtension;
+import org.eclipse.actf.ai.navigator.extension.MediaControlExtension;
+import org.eclipse.actf.ai.navigator.extension.ScreenReaderExtension;
+import org.eclipse.actf.ai.navigator.util.MessageFormatter;
+import org.eclipse.actf.ai.navigator.views.NavigatorTreeView;
+import org.eclipse.actf.ai.navigator.voice.VoiceManager;
+import org.eclipse.actf.ai.voice.IVoice;
+import org.eclipse.actf.ai.voice.IVoiceEventListener;
+import org.eclipse.actf.model.IModelServiceHolder;
+import org.eclipse.actf.model.IWebBrowserACTF;
+import org.eclipse.actf.util.ApplicationArgumentUtil;
+import org.eclipse.actf.util.BrowserLaunch;
+import org.eclipse.actf.util.ui.PlatformUIUtil;
+import org.eclipse.actf.util.vocab.IProposition;
+import org.eclipse.actf.util.vocab.Vocabulary;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+
+public abstract class NavigatorImpl implements INavigatorUI, IVoiceEventListener {
+    private static boolean isDemo = ApplicationArgumentUtil.isAvailable("-demo");
+
+    // private final WebEventListener webEventListener;
+
+    private IWebBrowserACTF webBrowser;
+
+    private INVM3Mediator nvm3Mediator;
+
+    private ITreeManager treeManager;
+
+    private final int maxRetry;
+
+    private final int retryInterval;
+
+    private Display display;
+
+    private IManipulator.Mode mode;
+    
+
+    public NavigatorImpl(WebEventListener webEventListener, IWebBrowserACTF webBrowser, int maxRetry, int retryInterval) {
+        // this.webEventListener = webEventListener;
+        this.webBrowser = webBrowser;
+        this.maxRetry = maxRetry;
+        this.retryInterval = retryInterval;
+        this.display = Display.getCurrent();
+    }
+
+    public ITreeManager getTreeManager() {
+        return treeManager;
+    }
+
+    private NavigatorTreeView getNavigatorTreeView() {
+        return NavigatorPlugin.getDefault().getNavigatorTreeView();
+    }
+
+    private void sleep(int interval) {
+        long startTime = System.currentTimeMillis();
+        while (true) {
+            boolean busy = display.readAndDispatch();
+            if (!busy)
+                display.sleep();
+            if ((System.currentTimeMillis() - startTime) > interval)
+                break;
+        }
+    }
+    
+    // -----------------------------------------------------------------------
+    // Sound Util
+    // -----------------------------------------------------------------------
+    
+    private void beep() {
+        Toolkit.getDefaultToolkit().beep();
+    }
+
+    private static IAudioPipe audio;
+
+    static {
+        if (true) {
+            // TODO replace audio file
+            URL url = NavigatorPlugin.getDefault().getBundle().getResource("waiting.wav");
+            IAudioReader reader = AudioFactory.createAudioReader(url);
+            IAudioWriter writer = AudioFactory.createDefaultWriter();
+            audio = AudioFactory.createLoopedAudioPipe(reader, writer);
+            audio.setBufferSize(100);
+            audio.setInterval(1);
+            audio.prepare();
+        }
+    }
+    
+    private void startProgress() {
+        startProgress(2000);
+    }
+    private void startProgress(int msec) {
+        audio.start(msec);
+    }
+    
+    private void endProgress() {
+        audio.stop();
+    }
+
+    // -----------------------------------------------------------------------
+    // Voice
+    // -----------------------------------------------------------------------
+
+    private final VoiceManager voiceManager = new VoiceManager(this);
+
+    protected MessageFormatter getMessageFormatter() {
+        return voiceManager.getMessageFormatter();
+    }
+
+    private VoiceManager getVoiceManager() {
+        return voiceManager;
+    }
+
+    public void indexReceived(int index) {
+        if (speakAllMode && (index == -1)) {
+            display.asyncExec(new Runnable() {
+                public void run() {
+                    traverseDownAll();
+                }
+            });
+        } else if (index == -2) {
+            display.asyncExec(new Runnable() {
+                public void run() {
+                    stopSpeak();
+                }
+            });
+        }
+    }
+    
+    // -----------------------------------------------------------------------
+    // Speak basic
+    // -----------------------------------------------------------------------
+
+    protected void speakWithFormat(String str) {
+        speakWithFormat(str, false);
+    }
+    
+    protected void speakWithFormat(String str, boolean flush) {
+        speak(getMessageFormatter().mes(str), flush, true);
+    }
+
+    protected void speak(String str, boolean flush) {
+        speak(str, flush, true);
+    }
+    
+    protected void speak(String str, boolean flush, boolean maleVoice) {
+        if (speakAllMode) {
+            getVoiceManager().speakWithCallback(str, flush, maleVoice);
+        } else {
+            getVoiceManager().speak(str, flush, maleVoice);
+        }
+    }
+    
+    private boolean speakAllMode = false;
+
+    private void stopSpeakAll() {
+        speakAllMode = false;
+    }
+
+    public void speakAll() {
+        speakAllMode = true;
+        try {
+            speakActiveItem(true, false, JumpMode.NONE);
+        } catch (TreeManagerException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void traverseDownAll() {
+        moveCmd(new Command() {
+            public int run(int r) throws TreeManagerException {
+                return treeManager.traverse(false);
+            }
+
+            public void after(int st) throws TreeManagerException {
+                afterMoveForSpeakAll(st, "Navigator.BOTTOM");
+            }
+        });
+    }
+
+    private void afterMoveForSpeakAll(int st, String notMoved) throws TreeManagerException {
+        if ((st & ITreeManager.CLICKED) != 0) {
+            speakWithFormat("Navigator.CLICK");
+        }
+        if ((st & ITreeManager.MOVED) != 0) {
+            speakActiveItem(true, false, JumpMode.NONE);
+            sayLevel();
+        } else {
+            speakAllMode = false;
+            sayLevel(notMoved);
+        }
+    }
+
+    public void stopSpeak() {
+        endProgress();
+        stopSpeakAll();
+        getVoiceManager().stop();
+    }
+
+
+    // -----------------------------------------------------------------------
+    // Say, speak
+    // -----------------------------------------------------------------------
+    
+    private void sayMode(IManipulator.Mode mode) {
+        speakWithFormat(mode.name + " mode");
+    }
+    
+    private void sayLevel(String prefix) throws TreeManagerException {
+        String mes = getMessageFormatter().mes(prefix);
+        speak(mes + " " + getLevelMessage(), true, true);
+    }
+    
+    private String getLevelMessage() throws TreeManagerException {
+        int level = treeManager.getLevel();
+        String mesLv;
+        if (level > 0) {
+            mesLv = getMessageFormatter().mes("Navigator.LEVEL", new Object[] { new Integer(level) });
+        } else {
+            mesLv = getMessageFormatter().mes("Navigator.TOP_LEVEL");
+        }
+        return mesLv;
+    }
+
+    private void sayLevel() throws TreeManagerException {
+        // speak(getLevelMessage(), false, true);
+    }
+
+    private void sayRetrial() {
+        speak(getMessageFormatter().mes("Navigator.UNDONE"), true, true);
+    }
+
+    private void speakVideoInfo(boolean flag) throws TreeManagerException {
+        IVideoControl vc = treeManager.getVideoControl();
+        if (flag || vcCount != vc.getCount()) {
+            vcCount = vc.getCount();
+            if (vcCount == 0) {
+                speakWithFormat("Navigator.NOVIDEO");
+            } else if (vcCount == 1) {
+                speakWithFormat("Navigator.SINGLEVIDEO");
+            } else {
+                String text = getMessageFormatter().mes("Navigator.VIDEOCOUNT", new Object[] { vcCount });
+                speak(text, false, false);
+            }
+        }
+    }
+
+    private void speakSoundInfo(boolean flag) throws TreeManagerException {
+        ISoundControl sc = treeManager.getSoundControl();
+        if (flag || scCount != sc.getCount()) {
+            scCount = sc.getCount();
+            if (scCount == 0) {
+                speakWithFormat("Navigator.NOSOUND");
+            } else if (scCount == 1) {
+                speakWithFormat("Navigator.SINGLESOUND");
+            } else {
+                String text = getMessageFormatter().mes("Navigator.SOUNDCOUNT", new Object[] { scCount });
+                speak(text, false, false);
+            }
+        }
+    }
+
+    private int vcCount, scCount;
+
+    private boolean mcAvailable = false;
+
+    private void speakMediaInfo() throws TreeManagerException {
+        IVideoControl vc = treeManager.getVideoControl();
+
+        StringBuffer buf = new StringBuffer();
+        if (vc.getCount() > 0) {
+            buf.append(getMessageFormatter().mes("Navigator.VIDEO_AT", 
+                    formatTime(vc.getCurrentPosition())));
+        }
+        if (vc.getTotalLength() > 0) {
+            buf.append(getMessageFormatter().mes("Navigator.VIDEO_TOTAL", 
+                    formatTime(vc.getTotalLength())));
+        }
+
+        vcCount = vc.getCount();
+        if (vcCount == 0) {
+            speakWithFormat("Navigator.NOVIDEO");
+        } else if (vcCount == 1) {
+            speakWithFormat("Navigator.SINGLEVIDEO");
+        } else {
+            int index = vc.getIndex();
+            String text = getMessageFormatter().mes("Navigator.VIDEOINDEX", new Object[] { index, vcCount });
+            speak(text, false, false);
+        }
+
+        speak(buf.toString(), false, false);
+    }
+
+    private String formatTime(double currentPosition) {
+        int s = (int) currentPosition;
+        int m = s / 60;
+        s %= 60;
+        int h = s / 60;
+        m %= 60;
+        
+        if (h > 0) {
+            return getMessageFormatter().mes("Navigator.HH_MM_SS", new Object[] { s, m, h });
+        } else if (m > 0) {
+            return getMessageFormatter().mes("Navigator.MM_SS", new Object[] { s, m });
+        }
+        return getMessageFormatter().mes("Navigator.SS", new Object[] { s });
+    }
+    
+    private void speakPageInfo() {
+        try {
+            speakVideoInfo(false);
+            speakSoundInfo(false);
+
+            MediaControlExtension.speakInfo(false);
+            
+            /*
+            if (!mcAvailable && MediaControlExtension.isAvailable()) {
+                speakWithFormat("AudioDescription.available");
+                mcAvailable = true;
+            }*/
+
+        } catch (TreeManagerException e) {
+        }
+    }
+
+    private void sayNVM3Name(String nvm3Name, boolean flag) {
+        String text = getMessageFormatter().mes("Navigator.FENNEC_NAME",
+                                           new Object[] { nvm3Name });
+        speak(text, flag, true);
+    }
+
+    private void sayNoNVM3(boolean flag) {
+        speakWithFormat("Navigator.NO_FENNEC", flag);
+    }
+
+    public void speakCurrentStatus() {
+        sayMode(mode);
+        try {
+            speakActiveItem(false, true, JumpMode.NONE);
+            speakPageInfo();
+        } catch (TreeManagerException e) {
+        }
+    }
+
+    public void speakMediaStatus() {
+        try {
+            speakMediaInfo();
+        } catch (TreeManagerException e) {
+        }
+    }
+
+    public void speakTab(boolean flush) {
+        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+        IWorkbenchPage page = window.getActivePage();
+        IEditorPart editor = page.getActiveEditor();
+
+        IEditorReference[] erefs = page.getEditorReferences();
+        boolean found = false;
+        int index = 0, total = 0;
+        for (int i = 0; i < erefs.length; i++) {
+            if (!found)
+                index++;
+            total++;
+            if (erefs[i].getEditor(false).equals(editor)) {
+                found = true;
+            }
+        }
+
+        String buf = "";
+        if (total == 1)
+            buf = editor.getTitle();
+        else
+            buf = getMessageFormatter().mes("Navigator.MOVE_TAB", editor.getTitle(), index, total);
+
+        speak(buf, flush, false);
+        stopSpeakAll();
+    }
+    
+
+    private ITreeItem lastHighlighted;
+
+    private void highlight(ITreeItem item) {
+        if (lastHighlighted != null) {
+            try {
+                lastHighlighted.unhighlight();
+            } catch (TreeManagerException e) {
+            }
+        }
+        try {
+            item.highlight();
+        } catch (TreeManagerException e) {
+        }
+        lastHighlighted = item;
+        sleep(1);
+    }
+    
+    protected enum JumpMode {
+        HEADING, 
+        LISTITEM_TOP, 
+        LINK, 
+        OBJECT, 
+        BLOCK, 
+        INPUT, 
+        LISTITEM_BOTTOM, 
+        ACCESSKEY, 
+        MEDIA,
+        ALTALABLE,
+        NONE,
+    }
+
+    private boolean listJumping = false;
+
+    private void speakActiveItem(boolean flush, boolean verbose, JumpMode jumpMode) throws TreeManagerException {
+        endProgress();
+        ITreeItem item = treeManager.getActiveItem();
+        ITreeItem[] siblings = treeManager.getSiblings();
+
+        final MessageFormatter mf = getMessageFormatter();
+        if (item == null)
+            return;
+        // update view
+        highlight(item);
+        getNavigatorTreeView().showItem(item);
+
+        // get item's UI string
+        int idx = item.getNth();
+        int st = intervalStart(siblings, idx);
+        int end = intervalEnd(siblings, idx);
+        StringBuffer bufUIStr = new StringBuffer();
+        for (int i = st; i <= end; i++) {
+            if (i < siblings.length)
+                bufUIStr.append(siblings[i].getUIString());
+            else
+                System.err.println("st " + st + ", end " + end + ", siblings.length " + siblings.length);
+        }
+        char accessKey = item.getAccessKey();
+
+        String uiStr = bufUIStr.toString();
+        String label = item.getFormLabel();
+        String speak = uiStr;
+
+        boolean isLink = Vocabulary.isLink().eval(item);
+        int hl = item.getHeadingLevel();
+        boolean isHeading = hl > 0;
+        boolean isListItem = Vocabulary.isListItem().eval(item);
+        boolean isListTop = Vocabulary.isListTop().eval(item);
+        boolean isCheckbox = Vocabulary.isCheckbox().eval(item);
+        boolean isRadio = Vocabulary.isRadio().eval(item);
+        boolean isCombobox = Vocabulary.isCombobox().eval(item);
+        //boolean isMultiSelectable = Vocabulary.isMultiSelectable().eval(item);
+        boolean isButton = Vocabulary.isButton().eval(item);
+        boolean isPassword = Vocabulary.isPassword().eval(item);
+        boolean isTextbox = Vocabulary.isTextbox().eval(item);
+        boolean isTextarea = Vocabulary.isMultilineEdit().eval(item);
+        boolean isFileEdit = Vocabulary.isFileEdit().eval(item);
+        boolean isChecked = Vocabulary.isChecked().eval(item);
+        boolean isClickable = Vocabulary.isClickable().eval(item);
+        boolean isFlashTopNode = Vocabulary.isFlashTopNode().eval(item);
+        boolean isFlashLastNode = Vocabulary.isFlashLastNode().eval(item);
+        boolean isMedia = Vocabulary.isMedia().eval(item);
+        
+        
+
+        if (isButton) {
+            String widget = mf.mes("Navigator.BUTTON");
+            if (uiStr.matches("[0-9]+")) {
+                speak = mf.concat(widget, uiStr);
+            } else {
+                speak = mf.concat(uiStr, widget);
+            }
+        } else if (isLink) {
+            boolean isVisitedLink = Vocabulary.isVisitedLink().eval(item);
+            String widget = mf.mes(isVisitedLink ? "Navigator.VISITED_LINK" : "Navigator.LINK");
+            if (jumpMode == JumpMode.LINK) {
+                speak = mf.concat(uiStr, widget);
+            } else {
+                speak = mf.concat(widget, uiStr);
+            }
+        } else if (isPassword) {
+            if (uiStr.length() > 0)
+                uiStr = mf.mes("Navigator.PASSWORD_STAR3");
+            String widget = mf.mes("Navigator.PASSWORD");
+            if (jumpMode == JumpMode.LINK) {
+                speak = mf.concat(label, widget, uiStr);
+            } else {
+                speak = mf.concat(uiStr, widget);
+            }
+        } else if (isFileEdit) {
+            String widget = mf.mes("Navigator.FILE_UPLOAD");
+            if (jumpMode == JumpMode.LINK) {
+                speak = mf.concat(label, widget, uiStr);
+            } else {
+                speak = mf.concat(widget, uiStr);
+            }
+        } else if (isTextarea) {
+            String widget = mf.mes((uiStr.length() == 0) ? "Navigator.TEXTAREA_EMPTY" : "Navigator.TEXTAREA");
+            if (jumpMode == JumpMode.LINK) {
+                speak = mf.concat(label, widget);
+            } else {
+                speak = widget;
+            }
+        } else if (isTextbox) {
+            String widget = mf.mes("Navigator.TEXTBOX");
+
+            if (jumpMode == JumpMode.LINK) {
+                speak = mf.concat(label, widget, uiStr);
+            } else {
+                speak = mf.concat(widget, uiStr);
+            }
+        } else if (isCheckbox) {
+            String widget = mf.mes("Navigator.CHECKBOX");
+            String checked = mf.mes(isChecked ? "Navigator.CHECKED" : "Navigator.NOT_CHECKED");
+
+            if (jumpMode == JumpMode.LINK) {
+                speak = mf.concat(label, widget, checked);
+            } else {
+                speak = mf.concat(widget, checked, label);
+            }
+        } else if (isRadio) {
+            String widget = mf.mes("Navigator.RADIO");
+            String checked = mf.mes(isChecked ? "Navigator.CHECKED" : "Navigator.NOT_CHECKED");
+
+            int index = item.getRadioIndex();
+            int total = item.getRadioTotal();
+            String nm = "";
+            if (index != 0 && total != 0) {
+                nm = mf.mes("Navigator.N_OF_M", index, total);
+            }
+
+            if (jumpMode == JumpMode.LINK) {
+                speak = mf.concat(label, widget, checked, nm);
+            } else {
+                speak = mf.concat(widget, checked, nm, label);
+            }
+        } else if (isCombobox) {
+            int[] indices = item.getSelectedIndices();
+            int total = item.getOptionsCount();
+
+            if (indices.length > 0) {
+                StringBuffer sb = new StringBuffer();
+                for (int i = 0; i < indices.length; i++) {
+                    sb.append(mf.mes("Navigator.COMBO_BOX") + " ");
+                    sb.append(item.getOptionTextAt(indices[i]));
+                    sb.append(" " + mf.mes("Navigator.N_OF_M", indices[i] + 1, total) + " ");
+                }
+                speak = mf.concat(uiStr, sb.toString());
+            } else {
+                speak = mf.concat(uiStr, mf.mes("Navigator.NO_SELECTION"));
+            }
+            if (jumpMode == JumpMode.LINK) {
+                speak = mf.concat(label, uiStr);
+            }
+        } else if (isFlashTopNode) {
+            boolean isMSAAFlash = Vocabulary.isMSAAFlash().eval(item);
+            if (isMSAAFlash)
+                speak = mf.concat(speak, mf.mes("Navigator.MSAA_FLASH_CONTENT"));
+            else
+                speak = mf.concat(speak, mf.mes("Navigator.FLASH_CONTENT"));
+        } else if (isFlashLastNode) {
+            speak = mf.mes("Navigator.FLASH_END");
+        }
+        if (speak.length() == 0) {
+            if (isClickable) {
+                speak = "Clickable";
+            }
+        }
+        if (isHeading) {
+            String widget = mf.mes("Navigator.HEADING");
+            String level = mf.mes("Navigator.LEVEL", hl);
+
+            if (jumpMode == JumpMode.HEADING) {
+                speak = mf.concat(speak, widget, level);
+            } else {
+                speak = mf.concat(widget, level, speak);
+            }
+        }
+
+        if (isMedia) {
+            String media = mf.mes("Navigator.MEDIA");
+            speak = mf.concat(media, speak);
+        }
+
+        if (isListItem) {
+            int index = item.getListIndex();
+            int total = item.getListTotal();
+            if (total > 1) {
+                String nm = mf.mes("Navigator.N_OF_M", index, total);
+                speak = mf.concat(speak, nm);
+                if (listJumping && ((jumpMode == JumpMode.LISTITEM_TOP && index == 1) //
+                        || (jumpMode == JumpMode.LISTITEM_BOTTOM && index == total))) {
+                    beep();
+                }
+                listJumping = true;
+            }
+        } else {
+            listJumping = false;
+        }
+        if (isListTop) {
+            ITreeItem[] children = item.getChildItems();
+            int n = 0;
+            for (int i = 0; i < children.length; i++) {
+                if (Vocabulary.isListItem().eval(children[i])) {
+                    n++;
+                }
+            }
+            speak = mf.concat(speak, uiStr, mf.mes("Navigator.LIST_TOP", n));
+        }
+        
+        if (accessKey != 0) {
+            speak = mf.concat(speak, mf.mes("Navigator.ACCESSKEY_GUIDE", accessKey));
+        }
+
+        if (verbose) {
+            String desc = item.getDescription();
+            if ((desc != null) && (desc.length() > 0)) {
+                speak = mf.concat(speak, desc);
+            }
+        }
+
+        speak(speak, flush, !isLink);
+    }
+
+    private int intervalStart(ITreeItem[] siblings, int st) {
+        if (siblings == null)
+            return 0;
+        for (st = st - 1;; st--) {
+            if (st < 0 || siblings.length <= st)
+                return 0;
+            if (!Vocabulary.isConnectable().eval(siblings[st]))
+                return st + 1;
+        }
+    }
+
+    private int intervalEnd(ITreeItem[] siblings, int end) {
+        if (siblings == null)
+            return 0;
+        for (; end < siblings.length; end++) {
+            if (!Vocabulary.isConnectable().eval(siblings[end]))
+                return end;
+        }
+        return end - 1;
+    }
+
+    // ---------------------------------------------------------------------
+    // Mode Control
+    // ---------------------------------------------------------------------
+    
+    // Used by NavigatorImplExSub
+    protected void setMode(IManipulator.Mode mode) {
+        if (this.mode == mode)
+            return;
+        if (this.mode != null) {
+            //sayMode(mode);
+        }
+        this.mode = mode;
+        ManipulatorExtension.setMode(mode);
+        getNavigatorTreeView().setMode(mode);
+        switch (mode.code) {
+        case IManipulator.TREE_NAVIGATION_MODE_CODE:
+            ScreenReaderExtension.screenReaderOff();
+            if (ScreenReaderExtension.isAvailable()) {
+                webBrowser.showAddressText(false);
+            }
+            break;
+        case IManipulator.FORM_INPUT_MODE_CODE:
+        default:
+            ScreenReaderExtension.screenReaderOn();
+            break;
+        }
+    }
+
+    private void enterFormInputMode(ITreeItem item) {
+        setMode(IManipulator.FORM_INPUT_MODE);
+        item.setFocus();
+    }
+
+    private void enterFormInputMode() {
+        setMode(IManipulator.FORM_INPUT_MODE);
+    }
+
+    // --------------------------------------------------------------------------------
+    // NVM3 Control
+    // --------------------------------------------------------------------------------
+
+    private int currentNVM3Idx = 1;
+    
+    private INVM3Entry currentEntry = null;
+    
+    protected INVM3Entry getCurrentEntry() {
+        return currentEntry;
+    }
+
+    private void selectNVM3(boolean next, boolean flush) {
+        selectNVM3(next, flush, true);
+    }
+    private void selectNVM3(boolean next, boolean flush, boolean sayFlag) {
+        if (lastHighlighted != null) {
+            try {
+                lastHighlighted.unhighlight();
+            } catch (TreeManagerException e1) {
+            }
+        }
+
+        INVM3Entry[] entries = nvm3Mediator.getNVM3Entries();
+        if (next) {
+            if (entries.length == 0) {
+                speakWithFormat("Navigator.NO_OTHER_FENNEC", true);
+                return;
+            }
+            currentNVM3Idx++;
+        }
+        if (currentNVM3Idx > entries.length)
+            currentNVM3Idx = 0;
+        try {
+            restoreLocation(getLocation());
+            if (currentNVM3Idx == 0) {
+                initNVM3(null, flush, sayFlag);
+            } else {
+                initNVM3(entries[currentNVM3Idx - 1], flush, sayFlag);
+            }
+            speakActiveItem(false, false, JumpMode.NONE);
+        } catch (TreeManagerException e) {
+        }
+    }
+
+    protected void selectUserNVM3() {
+        if (lastHighlighted != null) {
+            try {
+                lastHighlighted.unhighlight();
+            } catch (TreeManagerException e1) {
+            }
+        }
+
+        INVM3Entry[] entries = nvm3Mediator.getNVM3Entries();
+        for (int i = 0; i < entries.length; i++) {
+            if (entries[i].isUserEntry()) {
+                try {
+                    restoreLocation(getLocation());
+                    initNVM3(entries[i], true, true);
+                    speakActiveItem(false, false, JumpMode.NONE);
+                } catch (TreeManagerException e) {
+                }
+                return;
+            }
+        }
+        speakWithFormat("Navigator.NO_OTHER_FENNEC", true);
+        return;
+    }
+
+    public void selectNextNVM3() {
+        selectNVM3(true, true);
+    }
+    
+    void setNVM3Mediator(INVM3Mediator nvm3Mediator) {
+        if (this.nvm3Mediator != null) {
+            this.nvm3Mediator.release();
+        }
+        this.nvm3Mediator = nvm3Mediator;
+        // TODO
+        this.treeManager = null;
+        this.lastHighlighted = null;
+        getNavigatorTreeView().clearItem();
+    }
+
+
+    private String getNVM3Name(INVM3Entry entry) {
+        if (entry == null)
+            return getMessageFormatter().mes("Navigator.NO_FENNEC_MESSAGE");
+        String text = entry.getDocumentation();
+        if ((text != null) && (text.length() > 0))
+            return text;
+        return getMessageFormatter().mes("Navigator.NO_FENNEC_NAME");
+    }
+
+    private void initNVM3(INVM3Entry entry, boolean flag, boolean sayFlag) throws TreeManagerException {
+        currentEntry = entry;
+        treeManager = nvm3Mediator.newTreeManager(entry);
+        String nvm3Name = getNVM3Name(entry);
+        if (sayFlag) {
+            if (entry == null) {
+                sayNoNVM3(flag);
+            } else {
+                sayNVM3Name(nvm3Name, flag);
+            }
+        }
+        getNavigatorTreeView().showNVM3Name(nvm3Name);
+        setMode(IManipulator.TREE_NAVIGATION_MODE);
+        try {
+            treeManager.initialize();
+            if (sayFlag)
+                speakPageInfo();
+        } catch (TreeManagerException e) {
+            if (sayFlag)
+                speakWithFormat(e.getMessage());
+        }
+
+        if (!skipToAnchor(webBrowser.getURL())) {
+            if (locationToBeRestored != null) {
+                try {
+                    treeManager.moveToLocation(locationToBeRestored);
+                } catch (TreeManagerException e) {
+                }
+                locationToBeRestored = null;
+            }
+        }
+    }
+
+    public void startNavigation(IWebBrowserACTF webBrowser) {
+        startNavigation(webBrowser, true);
+    }
+
+    public void startNavigation(IWebBrowserACTF webBrowser, boolean sayFlag) {
+        endProgress();
+        if (ScreenReaderExtension.isAvailable() && isLeftViewsShown() && !isDemo) {
+            toggleLeftViewsShowing();
+        }
+
+        if (sayFlag)
+            speakWithFormat("Navigator.STARTNAVIGATION");
+
+        try {
+            initNVM3(nvm3Mediator.getDefaultNVM3Entry(), false, sayFlag);
+            currentNVM3Idx = 1;
+            if (sayFlag) {
+                speakActiveItem(false, false, JumpMode.NONE);
+            }
+        } catch (Exception e) {
+            // TODO Retry
+            e.printStackTrace();
+        }
+        ManipulatorExtension.setNavigator(this);
+        initializeMovieStartListener();
+    }
+
+    public void endNavigation() {
+        // TODO
+        ManipulatorExtension.disposeExtensions();
+    }
+
+    // --------------------------------------------------------------------------------
+    // Media Control
+    // --------------------------------------------------------------------------------
+
+    private ISoundControl prepareSoundControl() throws TreeManagerException {
+        ISoundControl sc = treeManager.getSoundControl();
+        if (sc.getCount() == 0) {
+            // say("Retry to control sound");
+            treeManager.analyze();
+            sc = treeManager.getSoundControl();
+        }
+        return sc;
+    }
+
+    private IVideoControl prepareVideoControl() throws TreeManagerException {
+        IVideoControl vc = treeManager.getVideoControl();
+        if (vc.getCount() == 0) {
+            // say("Retry to control video");
+            treeManager.analyze();
+            vc = treeManager.getVideoControl();
+        }
+        return vc;
+    }
+
+    public void muteMedia() {
+        ISoundControl sc;
+        try {
+            sc = prepareSoundControl();
+        } catch (TreeManagerException e) {
+            return;
+        }
+        if (sc == null)
+            return;
+        if (sc.getCount() == 0) {
+            speakWithFormat("Navigator.NOSOUND", true);
+            return;
+        }
+        sc.muteMedia();
+        if (sc.getVolumeState() == VolumeState.MUTE) {
+            speakWithFormat("Navigator.MUTEON", true);
+        } else {
+            speakWithFormat("Navigator.MUTEOFF", true);
+        }
+    }
+
+    public void volumeDownMedia() {
+        ISoundControl sc;
+        try {
+            sc = prepareSoundControl();
+        } catch (TreeManagerException e) {
+            return;
+        }
+        if (sc == null)
+            return;
+        if (sc.getCount() == 0) {
+            speakWithFormat("Navigator.NOSOUND", true);
+            return;
+        }
+        sc.volumeDownMedia();
+        if (sc.getVolumeState() == VolumeState.MIN || sc.getVolumeState() == VolumeState.MUTE) {
+            speakWithFormat("Navigator.VOLUMEMIN", true);
+        } else {
+            speakWithFormat("Navigator.VOLUMEDOWN", true);
+        }
+    }
+
+    public void minimalVolumeDownMedia() {
+        ISoundControl sc;
+        try {
+            sc = prepareSoundControl();
+        } catch (TreeManagerException e) {
+            return;
+        }
+        if (sc == null)
+            return;
+        if (sc.getCount() == 0) {
+            speakWithFormat("Navigator.NOSOUND", true);
+            return;
+        }
+        sc.minimalVolumeDownMedia();
+        if (sc.getVolumeState() == VolumeState.MIN || sc.getVolumeState() == VolumeState.MUTE) {
+            speakWithFormat("Navigator.VOLUMEMIN", true);
+        } else {
+            speakWithFormat("Navigator.VOLUMEDOWN", true);
+        }
+    }
+
+    public void volumeUpMedia() {
+        ISoundControl sc;
+        try {
+            sc = prepareSoundControl();
+        } catch (TreeManagerException e) {
+            return;
+        }
+        if (sc == null)
+            return;
+        if (sc.getCount() == 0) {
+            speakWithFormat("Navigator.NOSOUND", true);
+            return;
+        }
+        sc.volumeUpMedia();
+        if (sc.getVolumeState() == VolumeState.MAX) {
+            speakWithFormat("Navigator.VOLUMEMAX", true);
+        } else {
+            speakWithFormat("Navigator.VOLUMEUP", true);
+        }
+    }
+
+    public void minimalVolumeUpMedia() {
+        ISoundControl sc;
+        try {
+            sc = prepareSoundControl();
+        } catch (TreeManagerException e) {
+            return;
+        }
+        if (sc == null)
+            return;
+        if (sc.getCount() == 0) {
+            speakWithFormat("Navigator.NOSOUND", true);
+            return;
+        }
+        sc.minimalVolumeUpMedia();
+        if (sc.getVolumeState() == VolumeState.MAX) {
+            speakWithFormat("Navigator.VOLUMEMAX", true);
+        } else {
+            speakWithFormat("Navigator.VOLUMEUP", true);
+        }
+    }
+
+    private void sayVideoState(IVideoControl vc, boolean doPause, VideoState oldSt) {
+        VideoState st = vc.getVideoState();
+        switch (st) {
+        case STATE_PLAY:
+            if (doPause) {
+                if (oldSt == VideoState.STATE_PAUSE) {
+                    speakWithFormat("Navigator.RESUMEMEDIA");
+                } else if (oldSt == VideoState.STATE_PLAY) {
+                    speakWithFormat("Navigator.CANNOT_PAUSE");
+                }
+            } else {
+                speakWithFormat("Navigator.PLAYMEDIA");
+            }
+            break;
+        case STATE_STOP:
+            speakWithFormat("Navigator.STOPMEDIA");
+            break;
+        case STATE_PAUSE:
+            speakWithFormat("Navigator.PAUSEMEDIA");
+            break;
+        case STATE_WAITING:
+            speakWithFormat("Navigator.WAITINGMEDIA");
+            break;
+        case STATE_FASTFORWARD:
+            speakWithFormat("Navigator.FASTFORWARDMEDIA");
+            break;
+        case STATE_FASTREVERSE:
+            speakWithFormat("Navigator.FASTREVERSEMEDIA");
+            break;
+        case STATE_OTHER:
+            speakWithFormat("Navigator.NOT_AVAILABEL");
+            break;
+        }
+    }
+
+    public void previousTrack() {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void nextTrack() {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void stopMedia() {
+        try {
+            IVideoControl vc = prepareVideoControl();
+            VideoState st = vc.getVideoState();
+            vc.stopMedia();
+            sayVideoState(vc, false, st);
+        } catch (TreeManagerException e) {
+        }
+    }
+
+    public void playMedia() {
+        try {
+            IVideoControl vc = prepareVideoControl();
+            VideoState st = vc.getVideoState();
+            vc.playMedia();
+            sayVideoState(vc, false, st);
+        } catch (TreeManagerException e) {
+        }
+    }
+
+    public void pauseMedia() {
+        try {
+            IVideoControl vc = prepareVideoControl();
+            VideoState st = vc.getVideoState();
+            vc.pauseMedia();
+            sayVideoState(vc, true, st);
+        } catch (TreeManagerException e) {
+        }
+    }
+
+    public void fastReverse() {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void fastForward() {
+        // TODO Auto-generated method stub
+
+    }
+
+    // --------------------------------------------------------------------------------
+    // Tree Navigation
+    // --------------------------------------------------------------------------------
+
+
+    interface Command {
+        int run(int retry) throws TreeManagerException;
+        void after(int st) throws TreeManagerException;
+    }
+    
+    private void afterMove(int st, String notMoved) throws TreeManagerException {
+        afterMove(st, notMoved, null);
+    }
+
+    private void afterMove(int st, String notMoved, JumpMode jumpMode) throws TreeManagerException {
+        if ((st & ITreeManager.CLICKED) != 0) {
+            speakWithFormat("Navigator.CLICK");
+        }
+        else if ((st & ITreeManager.MOVED) != 0) {
+            speakActiveItem(true, false, jumpMode);
+            sayLevel();
+        } else {
+            sayLevel(notMoved);
+        }
+        speakAllMode = false;
+    }
+
+    private void moveCmd(Command cmd) {
+        if (treeManager == null)
+            return;
+        for (int i = 0; i < maxRetry; i++) {
+            try {
+                int st = cmd.run(i);
+                cmd.after(st);
+                return;
+            } catch (TreeManagerInterruptedException e) {
+                sayRetrial();
+                sleep(retryInterval);
+            } catch (TreeManagerException e) {
+            }
+        }
+        // navigationThread.sendCmd(cmd);
+    }
+    
+    private boolean skipToAnchor(String url) throws TreeManagerException {
+        int hashIdx = url.lastIndexOf('#');
+        if (hashIdx < 0)
+            return false;
+        hashIdx++;
+        if (hashIdx >= url.length())
+            return false;
+        String target = url.substring(hashIdx);
+        treeManager.skipToAnchor(target);
+        return true;
+    }
+
+    public void treeLeft() {
+        moveCmd(new Command() {
+            public int run(int r) throws TreeManagerException {
+                return treeManager.gotoParent();
+            }
+
+            public void after(int st) throws TreeManagerException {
+                afterMove(st, "Navigator.TOP");
+            }
+        });
+    }
+
+    public void treeRight() {
+        moveCmd(new Command() {
+            public int run(int r) throws TreeManagerException {
+                return treeManager.gotoFirstChild();
+            }
+
+            public void after(int st) throws TreeManagerException {
+                afterMove(st, "Navigator.NO_SUBITEMS");
+            }
+        });
+    }
+
+    public void treeUp() {
+        moveCmd(new Command() {
+            public int run(int r) throws TreeManagerException {
+                return treeManager.gotoPreviousSibling();
+            }
+
+            public void after(int st) throws TreeManagerException {
+                afterMove(st, "Navigator.TOP");
+            }
+        });
+    }
+
+    public void treeDown() {
+        moveCmd(new Command() {
+            public int run(int r) throws TreeManagerException {
+                return treeManager.gotoNextSibling();
+            }
+
+            public void after(int st) throws TreeManagerException {
+                afterMove(st, "Navigator.BOTTOM");
+            }
+        });
+    }
+
+    public void treeTop() {
+        moveCmd(new Command() {
+            public int run(int r) throws TreeManagerException {
+                return treeManager.gotoStartOfPage();
+            }
+
+            public void after(int st) throws TreeManagerException {
+                speakTab();
+                listJumping = false;
+                //afterMove(st, "Navigator.TOP");
+            }
+        });
+    }
+
+    public void treeBottom() {
+        moveCmd(new Command() {
+            public int run(int r) throws TreeManagerException {
+                return treeManager.gotoEndOfPage();
+            }
+
+            public void after(int st) throws TreeManagerException {
+                afterMove(st, "Navigator.BOTTOM");
+            }
+        });
+    }
+
+    public void traverseNodeDown() {
+        moveCmd(new Command() {
+            public int run(int r) throws TreeManagerException {
+                int moved;
+                int rFlag = 0;
+                do {
+                    moved = treeManager.traverse(false);
+                    rFlag |= moved;
+                } while (((moved & ITreeManager.LEVEL_CHANGED) == 0) && ((moved & ITreeManager.MOVED) != 0));
+                return moved | rFlag;
+            }
+
+            public void after(int st) throws TreeManagerException {
+                afterMove(st, "Navigator.BOTTOM");
+            }
+        });
+    }
+
+    public void traverseNodeUp() {
+        moveCmd(new Command() {
+            public int run(int r) throws TreeManagerException {
+                int moved;
+                int rFlag = 0;
+                do {
+                    moved = treeManager.traverse(true);
+                    rFlag |= moved;
+                } while (((moved & ITreeManager.LEVEL_CHANGED) == 0) && ((moved & ITreeManager.MOVED) != 0));
+                return moved | rFlag;
+            }
+
+            public void after(int st) throws TreeManagerException {
+                afterMove(st, "Navigator.TOP");
+            }
+        });
+    }
+
+    public void traverseDown() {
+        moveCmd(new Command() {
+            public int run(int r) throws TreeManagerException {
+                return treeManager.traverse(false);
+            }
+
+            public void after(int st) throws TreeManagerException {
+                if ((st & ITreeManager.MOVED) == 0) {
+                    beep();
+                    speak(getMessageFormatter().mes("Navigator.BOTTOM"), true, false);
+                    speakActiveItem(false, false, JumpMode.NONE);
+                } else {
+                    afterMove(st, "Navigator.BOTTOM");
+                }
+            }
+        });
+    }
+
+    public void traverseUp() {
+        moveCmd(new Command() {
+            public int run(int r) throws TreeManagerException {
+                return treeManager.traverse(true);
+            }
+
+            public void after(int st) throws TreeManagerException {
+                if ((st & ITreeManager.MOVED) == 0) {
+                    beep();
+                    speak(getMessageFormatter().mes("Navigator.TOP"), true, false);
+                    speakTab(false);
+                } else {
+                    afterMove(st, "Navigator.TOP");
+                }
+            }
+        });
+    }
+
+    public void click() {
+        try {
+            ITreeItem item = treeManager.getActiveItem();
+            if (item == null)
+                return;
+
+            // for restoring the class name of the item
+            item.unhighlight();
+
+            if (Vocabulary.isFileEdit().eval(item) || //
+                    Vocabulary.isCheckbox().eval(item) || //
+                    Vocabulary.isRadio().eval(item) || //
+                    !(Vocabulary.isInputable().eval(item) || Vocabulary.isSelectable().eval(item)) //
+            ) {
+                moveCmd(new Command() {
+                    public int run(int r) throws TreeManagerException {
+                        if (r == 0) {
+                            return treeManager.click(true);
+                        } else {
+                            return treeManager.click(false);
+                        }
+                    }
+
+                    public void after(int st) throws TreeManagerException {
+                        afterMove(st, "Navigator.CLICK");
+                    }
+                });
+                return;
+            }
+
+            if (false) {
+                enterFormInputMode(item);
+            } else {
+                setMode(IManipulator.KEYHOOK_DISABLED_MODE);
+                //item.setFocus();
+                Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+                boolean combo = Vocabulary.isCombobox().eval(item);
+
+                if (combo) {
+                    boolean multiple = Vocabulary.isMultiSelectable().eval(item);
+                    FormSelectDialog dialog = new FormSelectDialog(shell, item, multiple);
+
+                    if (dialog.open() == InputDialog.OK) {
+                        int[] indices = dialog.getSelectedIndices();
+                        int total = dialog.getLength();
+                        if (indices != null) {
+                            item.setSelectedIndices(indices);
+                            if (indices.length > 0) {
+                                StringBuffer sb = new StringBuffer();
+                                for (int i = 0; i < indices.length; i++) {
+                                    String text = dialog.getTextAt(indices[i]);
+                                    sb.append(text + " " + getMessageFormatter().mes("Navigator.N_OF_M", indices[i] + 1, total)
+                                              + " ");
+                                }
+                                speak(sb.toString(), true);
+                            } else {
+                                speakWithFormat("Navigator.COMBO_BOX_NOSELECTION");
+                            }
+                        }
+                    }
+                } else {
+                    boolean multiline = Vocabulary.isMultilineEdit().eval(item);
+                    boolean pass = Vocabulary.isPassword().eval(item);
+                    FormInputDialog dialog = new FormInputDialog(shell, item.getText(), multiline, pass);
+
+                    if (dialog.open() == InputDialog.OK) {
+                        String result = dialog.getResult();
+                        if (result != null) {
+                            item.setText(dialog.getResult());
+                            if (pass) {
+                                if (item.getText().length() > 0) {
+                                    speakWithFormat(getMessageFormatter().mes("Navigator.PASSWORD_STAR3"));
+                                }
+                            } else {
+                                speakWithFormat(item.getText());
+                            }
+                        }
+                    }
+                }
+                //item.setFocus();
+                setMode(IManipulator.TREE_NAVIGATION_MODE);
+            }
+
+            // for restoring the class name of the item
+            item.highlight();
+        } catch (TreeManagerException e) {
+            e.printStackTrace();
+        }
+    }
+
+    // --------------------------------------------------------------------------------
+    // Skip Navigation
+    // --------------------------------------------------------------------------------
+
+    // Used by NavigatorImplExSub
+    protected void findNext(final IProposition p, final JumpMode jumpMode) {
+        moveCmd(new Command() {
+            ILocation current;
+            public int run(int r) throws TreeManagerException {
+                startProgress();
+                current = getLocation();
+                return treeManager.findNext(p);
+            }
+
+            public void after(int st) throws TreeManagerException {
+                if ((st & ITreeManager.MOVED) == 0) {
+                    if (jumpMode != JumpMode.LISTITEM_TOP)
+                        beep();
+                    speak(getMessageFormatter().mes("Navigator.WRAPPING_TO_TOP"), true, false);
+                    moveCmd(new Command() {
+                        public int run(int r) throws TreeManagerException {
+                            treeManager.gotoStartOfPage();
+                            return treeManager.findNext(p);
+                        }
+
+                        public void after(int st) throws TreeManagerException {
+                            if ((st & ITreeManager.MOVED) == 0)
+                                treeManager.moveToLocation(current);
+                            afterFind(st, jumpMode);
+                            endProgress();
+                        }
+                    });
+                } else {
+                    afterMove(st, "Navigator.BOTTOM", jumpMode);
+                    endProgress();
+                }
+            }
+        });
+    }
+
+    private void afterFind(int st, JumpMode jumpMode) throws TreeManagerException {
+        afterFind(st, jumpMode, null);
+    }
+
+    private void afterFind(int st, JumpMode jumpMode, Object arg) throws TreeManagerException {
+        if ((st & ITreeManager.MOVED) != 0) {
+            speakActiveItem(false, false, jumpMode);
+        } else {
+            String buf = "";
+            if (jumpMode == JumpMode.HEADING)
+                buf = getMessageFormatter().mes("Navigator.NO_HEADING");
+            else if (jumpMode == JumpMode.BLOCK)
+                buf = getMessageFormatter().mes("Navigator.NO_BLOCK");
+            else if (jumpMode == JumpMode.INPUT)
+                buf = getMessageFormatter().mes("Navigator.NO_INPUT");
+            else if (jumpMode == JumpMode.LINK)
+                buf = getMessageFormatter().mes("Navigator.NO_LINK");
+            else if (jumpMode == JumpMode.LISTITEM_TOP || jumpMode == JumpMode.LISTITEM_BOTTOM)
+                buf = getMessageFormatter().mes("Navigator.NO_LISTITEM");
+            else if (jumpMode == JumpMode.OBJECT)
+                buf = getMessageFormatter().mes("Navigator.NO_OBJECT");
+            else if (jumpMode == JumpMode.MEDIA)
+                buf = getMessageFormatter().mes("Navigator.NO_MEDIA");
+            else if (jumpMode == JumpMode.ACCESSKEY)
+                buf = getMessageFormatter().mes("Navigator.NO_ACCESSKEY", arg);
+            else if (jumpMode == JumpMode.ALTALABLE)
+                buf = getMessageFormatter().mes("Navigator.NO_ALTALABLE");
+                
+
+            speak(buf, true, false);
+        }
+        speakAllMode = false;
+    }
+
+    // Used by NavigatorImplExSub
+    protected void findPrevious(final IProposition p, final JumpMode jumpMode) {
+        moveCmd(new Command() {
+            private ILocation current;
+            public int run(int r) throws TreeManagerException {
+                startProgress();
+                current = getLocation();
+                return treeManager.findPrevious(p);
+            }
+
+            public void after(int st) throws TreeManagerException {
+                if ((st & ITreeManager.MOVED) == 0) {
+                    if (jumpMode != JumpMode.LISTITEM_BOTTOM)
+                        beep();
+                    speak(getMessageFormatter().mes("Navigator.WRAPPING_TO_BOTTOM"), true, false);
+                    moveCmd(new Command() {
+                        public int run(int r) throws TreeManagerException {
+                            treeManager.gotoEndOfPageForFind();
+                            return treeManager.findPrevious(p);
+                        }
+
+                        public void after(int st) throws TreeManagerException {
+                            if ((st & ITreeManager.MOVED) == 0)
+                                treeManager.moveToLocation(current);
+                            afterFind(st, jumpMode);
+                            endProgress();
+                        }
+                    });
+                } else {
+                    afterMove(st, "Navigator.TOP", jumpMode);
+                    endProgress();
+                }
+            }
+        });
+    }
+
+    public void nextHeader() {
+        findNext(Vocabulary.isHeading(), JumpMode.HEADING);
+    }
+
+    public void previousHeader() {
+        findPrevious(Vocabulary.isHeading(), JumpMode.HEADING);
+    }
+
+    public void nextInputable() {
+        findNext(Vocabulary.or(Vocabulary.isInputable(), Vocabulary.isSelectable()), JumpMode.INPUT);
+    }
+
+    public void previousInputable() {
+        findPrevious(Vocabulary.or(Vocabulary.isInputable(), Vocabulary.isSelectable()), JumpMode.INPUT);
+    }
+
+    public void nextLink() {
+        findNext(Vocabulary.or(Vocabulary.isClickable(), Vocabulary.isInputable(), Vocabulary.isSelectable()),
+                JumpMode.LINK);
+    }
+
+    public void previousLink() {
+        findPrevious(Vocabulary.or(Vocabulary.isClickable(), Vocabulary.isInputable(), Vocabulary.isSelectable()),
+                JumpMode.LINK);
+    }
+
+    public void nextObject() {
+        findNext(Vocabulary.isEmbeddedObject(), JumpMode.OBJECT);
+    }
+
+    public void previousObject() {
+        findPrevious(Vocabulary.isEmbeddedObject(), JumpMode.OBJECT);
+    }
+
+    public void nextListItem() {
+        findNext(Vocabulary.isListItem(), JumpMode.LISTITEM_TOP);
+    }
+
+    public void previousListItem() {
+        findPrevious(Vocabulary.isListItem(), JumpMode.LISTITEM_BOTTOM);
+    }
+
+    public void nextBlock() {
+        findNext(Vocabulary.isBlockJumpPointF(), JumpMode.BLOCK);
+    }
+
+    public void previousBlock() {
+        findPrevious(Vocabulary.isBlockJumpPointB(), JumpMode.BLOCK);
+    }
+
+    public void nextMedia() {
+        findNext(Vocabulary.isMedia(), JumpMode.MEDIA);
+    }
+
+    public void previousMedia() {
+        findPrevious(Vocabulary.isMedia(), JumpMode.MEDIA);
+    }
+
+    public void nextHeader1() {
+        findNext(Vocabulary.and(Vocabulary.isHeading(), Vocabulary.isHeading1()), JumpMode.HEADING);
+    }
+
+    public void nextHeader2() {
+        findNext(Vocabulary.and(Vocabulary.isHeading(), Vocabulary.isHeading2()), JumpMode.HEADING);
+    }
+
+    public void nextHeader3() {
+        findNext(Vocabulary.and(Vocabulary.isHeading(), Vocabulary.isHeading3()), JumpMode.HEADING);
+    }
+
+    public void nextHeader4() {
+        findNext(Vocabulary.and(Vocabulary.isHeading(), Vocabulary.isHeading4()), JumpMode.HEADING);
+    }
+
+    public void nextHeader5() {
+        findNext(Vocabulary.and(Vocabulary.isHeading(), Vocabulary.isHeading5()), JumpMode.HEADING);
+    }
+
+    public void nextHeader6() {
+        findNext(Vocabulary.and(Vocabulary.isHeading(), Vocabulary.isHeading6()), JumpMode.HEADING);
+    }
+
+    public void previousHeader1() {
+        findPrevious(Vocabulary.and(Vocabulary.isHeading(), Vocabulary.isHeading1()), JumpMode.HEADING);
+    }
+
+    public void previousHeader2() {
+        findPrevious(Vocabulary.and(Vocabulary.isHeading(), Vocabulary.isHeading2()), JumpMode.HEADING);
+    }
+
+    public void previousHeader3() {
+        findPrevious(Vocabulary.and(Vocabulary.isHeading(), Vocabulary.isHeading3()), JumpMode.HEADING);
+    }
+
+    public void previousHeader4() {
+        findPrevious(Vocabulary.and(Vocabulary.isHeading(), Vocabulary.isHeading4()), JumpMode.HEADING);
+    }
+
+    public void previousHeader5() {
+        findPrevious(Vocabulary.and(Vocabulary.isHeading(), Vocabulary.isHeading5()), JumpMode.HEADING);
+    }
+
+    public void previousHeader6() {
+        findPrevious(Vocabulary.and(Vocabulary.isHeading(), Vocabulary.isHeading6()), JumpMode.HEADING);
+    }
+    
+    public void jumpToAccessKey(final char key) {
+        moveCmd(new Command() {
+            ILocation current;
+            public int run(int r) throws TreeManagerException {
+                startProgress();
+                current = getLocation();
+                return treeManager.findNext(Vocabulary.isAccessKey(key));
+            }
+
+            public void after(int st) throws TreeManagerException {
+                if ((st & ITreeManager.MOVED) == 0) {
+                    moveCmd(new Command() {
+                        public int run(int r) throws TreeManagerException {
+                            treeManager.gotoStartOfPage();
+                            return treeManager.findNext(Vocabulary.isAccessKey(key));
+                        }
+
+                        public void after(int st) throws TreeManagerException {
+                            if ((st & ITreeManager.MOVED) == 0){
+                                treeManager.moveToLocation(current);
+                                afterFind(st, JumpMode.ACCESSKEY, key);
+                            }
+                            else
+                                afterAccessKeyJump(st);
+                            endProgress();
+                        }
+                    });
+                } else {
+                    afterAccessKeyJump(st);
+                    endProgress();
+                }
+            }
+        });
+    }
+
+    private void afterAccessKeyJump(int st) throws TreeManagerException {
+        ITreeItem item = treeManager.getActiveItem();
+
+        boolean isCheckbox = Vocabulary.isCheckbox().eval(item);
+        boolean isRadio = Vocabulary.isRadio().eval(item);
+
+        if (isRadio || isCheckbox) {
+            click();
+            speakActiveItem(true, false, JumpMode.NONE);
+        } else {
+            afterMove(st, "Navigator.BOTTOM", JumpMode.ACCESSKEY);
+        }
+    }
+
+    public void showAccessKeyList() {
+        try {
+            setMode(IManipulator.KEYHOOK_DISABLED_MODE);
+            IAccessKeyList list = treeManager.getAccessKeyList();
+            Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+
+            AccessKeyListDialog dialog = new AccessKeyListDialog(shell, list);
+
+            if (dialog.open() == InputDialog.OK) {
+                char key = dialog.getSelectedKey();
+                if (key != 0)
+                    jumpToAccessKey(key);
+            }
+            setMode(IManipulator.TREE_NAVIGATION_MODE);
+        } catch (TreeManagerException e) {
+            e.printStackTrace();
+        }
+    }
+    
+
+    public void searchNext() {
+        Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+        SearchDialog dialog = new SearchDialog(shell);
+        dialog.setForward(true);
+        if (dialog.open() == Window.CANCEL || dialog.getString() == null || dialog.getString().length() == 0)
+            return;
+
+        moveCmd(new FindCommand(dialog.getString(), dialog.isForward(), dialog.isExact()));
+    }
+
+    public void searchPrevious() {
+        Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+        SearchDialog dialog = new SearchDialog(shell);
+        dialog.setForward(false);
+        if (dialog.open() == Window.CANCEL || dialog.getString() == null || dialog.getString().length() == 0)
+            return;
+
+        moveCmd(new FindCommand(dialog.getString(), dialog.isForward(), dialog.isExact()));
+    }
+
+    class FindCommand implements Command {
+        private String str;
+
+        private boolean direction;
+
+        private boolean exact;
+
+        FindCommand(String str, boolean direction, boolean exact) {
+            this.str = str;
+            this.direction = direction;
+            this.exact = exact;
+        }
+
+        public int run(int r) throws TreeManagerException {
+            if (direction)
+                return treeManager.findNext(Vocabulary.find(str, exact));
+            else
+                return treeManager.findPrevious(Vocabulary.find(str, exact));
+        }
+
+        public void after(int st) throws TreeManagerException {
+            if (direction)
+                afterMove(st, "Navigator.BOTTOM");
+            else
+                afterMove(st, "Navigator.TOP");
+        }
+    }
+
+    // --------------------------------------------------------------------------------
+    // Table Navigation
+    // --------------------------------------------------------------------------------
+
+    public void cellLeft() {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void cellRight() {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void cellUp() {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void cellDown() {
+        // TODO Auto-generated method stub
+
+    }
+
+    // --------------------------------------------------------------------------------
+    // Speech Speed Control
+    // --------------------------------------------------------------------------------
+
+    private void saySpeechSpeed(int speed) {
+        String mes = getMessageFormatter().mes("Navigator.SPEECHSPEED",
+                                          new Object[] { new Integer(speed) });
+        speakWithFormat(mes, true);
+    }
+
+    public void speechSpeedUp() {
+        int nextSpeed = getVoiceManager().getSpeed() + 10;
+        if (IVoice.SPEED_MAX < nextSpeed) {
+            speakWithFormat("Navigator.SPEECHSPEEDMAX", true);
+        } else {
+            getVoiceManager().setSpeed(nextSpeed);
+            saySpeechSpeed(nextSpeed);
+        }
+    }
+
+    public void speechSpeedDown() {
+        int nextSpeed = getVoiceManager().getSpeed() - 10;
+        if (IVoice.SPEED_MIN > nextSpeed) {
+            speakWithFormat("Navigator.SPEECHSPEEDMIN", true);
+        } else {
+            getVoiceManager().setSpeed(nextSpeed);
+            saySpeechSpeed(nextSpeed);
+        }
+    }
+
+    // --------------------------------------------------------------------------------
+    // Form input mode.
+    // --------------------------------------------------------------------------------
+
+    public void exitFormMode() {
+        setMode(IManipulator.TREE_NAVIGATION_MODE);
+    }
+
+    public void submitForm() {
+        // TODO Auto-generated method stub
+    }
+
+
+    // --------------------------------------------------------------------------------
+    // Location Management
+    // --------------------------------------------------------------------------------
+
+    public ILocation getLocation() {
+        if (treeManager == null)
+            return null;
+        try {
+            return treeManager.getCurrentLocation();
+        } catch (TreeManagerException e) {
+            return null;
+        }
+    }
+
+    private ILocation locationToBeRestored;
+
+    public void restoreLocation(ILocation location) {
+        locationToBeRestored = location;
+    }
+
+    // --------------------------------------------------------------------------------
+    // IMediaControl.IHandle delegation class
+    // --------------------------------------------------------------------------------
+    private class MediaControlHandle implements IMediaControl.IHandle {
+        public ISoundControl getSoundControl() {
+            if (treeManager == null)
+                return null;
+            try {
+                return treeManager.getSoundControl();
+            } catch (TreeManagerException e) {
+                return null;
+            }
+        }
+
+        public IVideoControl getVideoControl() {
+            if (treeManager == null)
+                return null;
+            try {
+                return treeManager.getVideoControl();
+            } catch (TreeManagerException e) {
+                return null;
+            }
+        }
+
+        public IVoice getVoice() {
+            return NavigatorImpl.this.getVoiceManager();
+        }
+
+        public IWebBrowserACTF getWebBrowser() {
+            return webBrowser;
+        }
+    }
+
+    private MediaControlHandle handle = new MediaControlHandle();
+
+    IMediaControl.IHandle getMediaControlHandle() {
+        return handle;
+    }
+
+    public void toggleDescriptionEnable() {
+        int result = MediaControlExtension.toggleEnable();
+        switch (result) {
+        case IMediaControl.TOGGLE_FAIL:
+            speakWithFormat("AudioDescription.noExtension");
+            break;
+        case IMediaControl.STATUS_ON:
+            speakWithFormat("AudioDescription.on");
+            break;
+        case IMediaControl.STATUS_OFF:
+            speakWithFormat("AudioDescription.off");
+            break;
+        case IMediaControl.STATUS_NOT_AVAILABLE:
+            speakWithFormat("AudioDescription.notAvailable");
+            break;
+        }
+    }
+
+    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+    MovieStartListener listener = null;
+
+    private void initializeMovieStartListener() {
+        IHandle mediaControlHandle = getMediaControlHandle();
+        final IVideoControl video = mediaControlHandle.getVideoControl();
+        if (listener != null) {
+            listener.stop();
+        }
+        listener = new MovieStartListener(video);
+        if (video != null) {
+            video.addEventListener(listener);
+        }
+    }
+
+    class MovieStartListener implements IMediaSyncEventListener {
+        IVideoControl video;
+
+        public MovieStartListener(IVideoControl video) {
+            this.video = video;
+        }
+
+        private boolean stopFlag = false;
+
+        public void stop() {
+            stopFlag = true;
+        }
+
+        public double getInterval() {
+            return 0.1;
+        }
+
+        private boolean topFlag = true;
+
+        public void run() {
+            if (stopFlag)
+                return;
+            double time = video.getCurrentPosition();
+            if (topFlag) {
+                // Heuristic solution.
+                // "stop" operation might locates nearly 0.001?. 0.1 avoids this situnation.
+                // 3.0 is a sentinel for waiting the completion of reloading and analyzing page content.
+                if (0.1 < time && time < 3.0) {
+                    speakWithFormat("Navigator.MOVIE_START");
+                    topFlag = false;
+                }
+            } else {
+                if (time < 0.1) {
+                    topFlag = true;
+                }
+            }
+        }
+    }
+
+    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+    public enum FocusTabResult {
+        STAY, CHANGED, NOTFOUND
+    }
+
+    public FocusTabResult focusTab() {
+        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+        IWorkbenchPage page = window.getActivePage();
+        IEditorReference[] erefs = page.getEditorReferences();
+        IEditorPart editor = page.getActiveEditor();
+
+        for (int i = 0; i < erefs.length; i++) {
+            IEditorPart part = erefs[i].getEditor(false);
+            IModelServiceHolder modelServiceHolder = (IModelServiceHolder) part;
+            IWebBrowserACTF wb = (IWebBrowserACTF) modelServiceHolder.getModelService();
+            if (wb == webBrowser) {
+                if (editor != part) {
+                    page.activate(part);
+                    return FocusTabResult.CHANGED;
+                }
+                return FocusTabResult.STAY;
+            }
+        }
+        return FocusTabResult.NOTFOUND;
+    }
+
+    public boolean isFocused() {
+        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+        IWorkbenchPage page = window.getActivePage();
+        IEditorReference[] erefs = page.getEditorReferences();
+        IEditorPart editor = page.getActiveEditor();
+
+        for (int i = 0; i < erefs.length; i++) {
+            IEditorPart part = erefs[i].getEditor(false);
+            IModelServiceHolder modelServiceHolder = (IModelServiceHolder) part;
+            IWebBrowserACTF wb = (IWebBrowserACTF) modelServiceHolder.getModelService();
+            if (wb == webBrowser) {
+                if (editor != part) {
+                    return false;
+                }
+                return true;
+            }
+        }
+        // Should be regarded as an error.
+        System.err.println("Internal Error: WebBrowser:" + webBrowser + " is not managed in the editor");
+        return false;
+    }
+
+    public String getCurrentURL() {
+        return webBrowser.getURL();
+    }
+
+    // ----------------------------------------------------------------
+    // Browser Control 
+    // ----------------------------------------------------------------
+
+    public void enterBrowserAddress() {
+        if (PlatformUIUtil.getActiveEditor() == null) {
+            speakWithFormat("Navigator.THERE_ARE_NO_TAB");
+            return;
+        }
+        
+        webBrowser.setFocusAddressText(true);
+        speakWithFormat("Navigator.ENTERBROWSERADDRESS", false);
+        enterFormInputMode();
+    }
+    
+    public void forceRestart(boolean flush) {
+        selectNVM3(false, flush);
+        
+        // TODO
+        MediaControlExtension.doDispose(getMediaControlHandle());
+        MediaControlExtension.start(getMediaControlHandle());
+        initializeMovieStartListener();
+    }
+
+    public void forceRestart() {
+        forceRestart(true);
+    }
+    
+    public void navigateRefresh() {
+        startProgress();
+        String buf = getMessageFormatter().mes("Navigator.REFRESH");
+        speak(buf, true, false);
+        webBrowser.navigateRefresh();
+    }
+
+    public void closeTab() {
+        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+        IWorkbenchPage page = window.getActivePage();
+        IEditorPart editor = page.getActiveEditor();
+
+        if (editor != null && editor instanceof IModelServiceHolder) {
+            ManipulatorExtension.setNavigator(null);
+            page.closeEditor(editor, false);
+        }
+    }
+
+    public void nextTab() {
+        gotoTab(1);
+    }
+
+    public void prevTab() {
+        gotoTab(-1);
+    }
+
+    private void gotoTab(int n) {
+        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+        IWorkbenchPage page = window.getActivePage();
+        IEditorReference[] erefs = page.getEditorReferences();
+        IEditorPart editor = page.getActiveEditor();
+
+        for (int i = 0; i < erefs.length; i++) {
+            if (erefs[i].getEditor(false).equals(editor)) {
+                n += i;
+
+                IEditorPart part;
+                if (n >= erefs.length) {
+                    part = erefs[0].getEditor(false);
+                } else if (n < 0) {
+                    part = erefs[erefs.length - 1].getEditor(false);
+                } else {
+                    part = erefs[n].getEditor(false);
+                }
+                if (part != null) {
+                    page.activate(part);
+                }
+            }
+        }
+        speakTab();
+    }
+
+
+    public void goBackward() {
+        webBrowser.goBackward();
+    }
+
+    public void goForward() {
+        webBrowser.goForward();
+    }
+
+    // It is used by the bridge.
+    public boolean gotoUrl(String url) {
+        webBrowser.navigate(url);
+        return true;
+    }
+    
+    public void launchBrowser() {
+        BrowserLaunch.launch(webBrowser.getURL());
+        String buf = getMessageFormatter().mes("Navigator.LAUNCH_DEFAULT_BROWSER", webBrowser.getURL());
+        speak(buf, true, false);
+    }
+
+    public boolean isLeftViewsShown() {
+        return getNavigatorTreeView().isShown();
+    }
+
+    public void toggleLeftViewsShowing() {
+        boolean result = getNavigatorTreeView().toggleViewShowing();
+        boolean result2 = MediaControlExtension.toggleViewShowing();
+        while (result != result2)
+            result2 = MediaControlExtension.toggleViewShowing();
+
+        if (result)
+            speakWithFormat("Navigator.VIEWS_ARE_OPEND", true);
+        else
+            speakWithFormat("Navigator.VIEWS_ARE_CLOSED", true);
+    }
+    
+    public void exportMetadata() {
+        if (getCurrentEntry() == null) {
+            speakWithFormat("Navigator.NO_ANNOTATION");
+            return;
+        }
+        
+        String[] ext = { "*.fnc" };
+        speakWithFormat("Navigator.EXPORT_ANNOTATION");
+        Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+        FileDialog fileDialog = new FileDialog(shell, SWT.OPEN);
+        fileDialog.setFilterExtensions(ext);
+        String path = fileDialog.open();
+
+        if (path != null) {
+            if (!path.endsWith(".fnc")) {
+                path = path+".fnc";
+            }
+            File dest = new File(path);
+            if (dest.exists()) {
+                String title = getMessageFormatter().mes("Navigator.OVERWRITE_CONFIRM");
+                String message = getMessageFormatter().mes("Navigator.OVERWRITE_MESSAGE", dest.getName());
+                boolean ret = MessageDialog.openQuestion(shell, title, message);
+                if (!ret)
+                    return;
+            }
+            if (getCurrentEntry().isUserEntry()) {
+                if (getCurrentEntry().export(dest)) {
+                    speakWithFormat("Navigator.EXPORT_IS_SUCCEEDED");
+                } else {
+                    speakWithFormat("Navigator.EXPORT_IS_FAILED");
+                }
+            }
+        }
+    }
+    
+    public void repairFlash() {
+        startProgress(0);
+        try {
+            treeManager.repairFlash();
+            speakWithFormat("Navigator.REPAIR_FINISHED");
+            restoreLocation(getLocation());
+            selectNVM3(false, false, false);
+        } catch (TreeManagerException e) {
+            e.printStackTrace();
+        }
+        endProgress();
+    }
+    
+    // ---------------------------------------------------------------
+    // Web Event
+    // ---------------------------------------------------------------
+    
+    public void speakTitle(String title) {
+        try {
+           speak(title, true, true);
+        } catch (Exception e) {
+        }
+    }
+    
+    public void speakTab() {
+        speakTab(true);
+    }
+
+    public void speakOpenTab() {
+        speakWithFormat(getMessageFormatter().mes("Navigator.NEW_TAB"));
+    }
+
+    public void navigateComplete() {
+        startProgress();
+    }
+
+    public void speakCloseTab(String title) {
+        speak(getMessageFormatter().mes("Navigator.CLOSE_TAB", title), true, true);
+    }
+
+    private long progressTimer = 0;
+
+    private static final int progressInterval = 2000;
+
+    private int prevPercent = -4;
+
+    void beforeNavigation(String uri) {
+        prevPercent = -4;
+    }
+
+    void progressChange(int progress, int progressMax) {
+        if (progressMax == 0)
+            return;
+        if (progress == -1) {
+            progressTimer = 0;
+            return;
+        }
+        long current = System.currentTimeMillis();
+
+        if (current - progressTimer > progressInterval * 5) {
+            prevPercent = -4;
+        }
+
+        if (progressMax < 10000) {
+            progressMax = 1000;
+        }
+        progressMax = (int) Math.pow(10, ((int) Math.log10(progressMax)) + 1);
+
+        int percent = (progress * 100) / progressMax;
+        if (progress * 10 == progressMax) {
+            percent = 100;
+        }
+
+        if ((progressTimer == 0) || ((current - progressTimer) >= progressInterval)) {
+            if (5 < percent && percent < 100) {
+                if (percent - prevPercent >= 5 || (prevPercent - percent > 10)) { // && prevPercent - percent < 50)) {
+                    sayProgress(percent);
+
+                    // System.out.println("Say " + percent);
+                    prevPercent = percent;
+                    progressTimer = current;
+                }
+            }
+        }
+        // System.out.println(progress + " / " + progressMax + ", " + percent + ", " + prevPercent);
+    }
+
+    private void sayProgress(int percent) {
+        if (percent < 0)
+            percent = 0;
+        else if (percent > 100)
+            percent = 100;
+        String mes = getMessageFormatter().mes("Navigator.PROGRESS", new Object[] { new Integer(percent) });
+        speak(mes, true, true);
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/NavigatorImplEx.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/NavigatorImplEx.java
new file mode 100644
index 0000000..6b81f4a
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/NavigatorImplEx.java
@@ -0,0 +1,292 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.navigator.impl;
+
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.fennec.treemanager.TreeManagerException;
+import org.eclipse.actf.ai.navigator.IManipulator;
+import org.eclipse.actf.ai.navigator.NavigatorPlugin;
+import org.eclipse.actf.ai.navigator.preferences.UserInfoPreferenceConstants;
+import org.eclipse.actf.ai.navigator.userinfo.IMetaDataModifier;
+import org.eclipse.actf.ai.navigator.userinfo.IUserInfoGenerator;
+import org.eclipse.actf.ai.navigator.userinfo.impl.AltInputDialog;
+import org.eclipse.actf.ai.navigator.userinfo.impl.AltTextEditor;
+import org.eclipse.actf.ai.navigator.userinfo.impl.AltTextGuesser;
+import org.eclipse.actf.ai.navigator.userinfo.impl.BrowserObserver;
+import org.eclipse.actf.ai.navigator.userinfo.impl.HeadingCanceller;
+import org.eclipse.actf.ai.navigator.userinfo.impl.LandmarkMaker;
+import org.eclipse.actf.ai.navigator.userinfo.impl.MetaDataModifier;
+import org.eclipse.actf.ai.navigator.util.ContentShortener;
+import org.eclipse.actf.ai.xmlstore.XMLStoreException;
+import org.eclipse.actf.model.IWebBrowserACTF;
+import org.eclipse.actf.util.ui.PlatformUIUtil;
+import org.eclipse.actf.util.vocab.Vocabulary;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+
+
+public class NavigatorImplEx extends NavigatorImpl {
+    private final IPreferenceStore preferenceStore;
+
+    private final ContentShortener contentShortener;
+
+    private final BrowserObserver observer;
+
+    private IWebBrowserACTF webBrowser;
+
+    public NavigatorImplEx(WebEventListener webEventListener,
+                              IWebBrowserACTF webBrowser,
+                              int maxRetry, int retryInterval) {
+        super(webEventListener, webBrowser, maxRetry, retryInterval);
+
+        this.webBrowser = webBrowser;
+        this.preferenceStore = NavigatorPlugin.getDefault().getPreferenceStore();
+        this.contentShortener = new ContentShortener(32, getMessageFormatter());
+        this.observer = new BrowserObserver(webBrowser);
+    }
+
+    public void saveUserInfo() {
+        if (getCurrentEntry() != null && !getCurrentEntry().isUserEntry()) {
+            speakUnavailableUserInfo();
+            return;
+        }
+        try {
+            IMetaDataModifier modifier = new MetaDataModifier();
+            // modifier.setGenerator(null);
+            modifier.setSite(observer.getTargetFilter());
+            modifier.setPageTitle(PlatformUIUtil.getActiveEditor().getTitle());
+            modifier.commit(true);
+            speakWithFormat("Navigator.ANNOTATION_IS_SAVED", true);
+        } catch (XMLStoreException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void guessAltText() {
+        if (getCurrentEntry() != null && !getCurrentEntry().isUserEntry()) {
+            speakUnavailableUserInfo();
+            return;
+        }
+        try {
+            ITreeItem item = getTreeManager().getCurrentRootItem();
+            setMode(IManipulator.KEYHOOK_DISABLED_MODE);
+            item.setFocus();
+            int k = guessAltTextIter(item);
+            if (k > 0) {
+                String mes = getMessageFormatter().mes("Navigator.ALT_TEXT_WERE_GUESSED", k);
+                speak(mes, true);
+                if (preferenceStore.getBoolean(UserInfoPreferenceConstants.AUTO_SAVE))
+                    saveUserInfo();
+                if (preferenceStore.getBoolean(UserInfoPreferenceConstants.AUTO_REFRESH))
+                    refresh();
+            }
+            else {
+                speakWithFormat("Navigator.THERE_IS_NO_MISSING_ALT_TEXT", true);
+            }
+            item.setFocus();
+            setMode(IManipulator.TREE_NAVIGATION_MODE);
+        } catch (TreeManagerException e) {
+            e.printStackTrace();
+        } catch (XMLStoreException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private int guessAltTextIter(ITreeItem item) throws XMLStoreException {
+        int k = 0;
+        ITreeItem[] v = item.getChildItems();
+
+        for (int i = 0; i < v.length; i++) {
+            ITreeItem c = v[i];
+            k += guessAltTextIter(c);
+        }
+
+        if (!Vocabulary.isAlterable().eval(item)) {
+            return k;
+        }
+
+        if (isGoodAltText(item.getUIString())) {
+            return k;
+        }
+
+        AltTextGuesser guesser = new AltTextGuesser(webBrowser, item);
+        String newText = guesser.guessByContext();
+
+        if (newText == null || newText.length() == 0) {
+            return k;
+        }
+
+        IMetaDataModifier modifier = new MetaDataModifier();
+        modifier.setGenerator(new AltTextEditor());
+        modifier.setSite(observer.getTargetFilter());
+        modifier.setPageTitle(PlatformUIUtil.getActiveEditor().getTitle());
+        modifier.setItem(item);
+        
+        modifier.setText(getMessageFormatter().mes("Navigator.ANNOTATION_LINK_TO", newText));
+        IUserInfoGenerator.Result result = modifier.commit(false);
+
+        if (result != IUserInfoGenerator.Result.NOTHING)
+            ++k;
+
+        return k;
+    }
+
+    private boolean isGoodAltText(String s) {
+        return s.length() > 0;
+    }
+
+    public void nextAlterable() {
+        if (getCurrentEntry() != null && !getCurrentEntry().isUserEntry()) {
+            speakUnavailableUserInfo();
+            return;
+        }
+        findNext(Vocabulary.isAlterable(), JumpMode.ALTALABLE);
+    }
+
+    public void previousAlterable() {
+        if (getCurrentEntry() != null && !getCurrentEntry().isUserEntry()) {
+            speakUnavailableUserInfo();
+            return;
+        }
+        findPrevious(Vocabulary.isAlterable(), JumpMode.ALTALABLE);
+    }
+
+    public void editAltText() {
+        if (getCurrentEntry() != null && !getCurrentEntry().isUserEntry()) {
+            speakUnavailableUserInfo();
+            return;
+        }
+
+        try {
+//            refresh();
+            ITreeItem item = getTreeManager().getActiveItem();
+            if (item == null)
+                return;
+            if (Vocabulary.isAlterable().eval(item)) {
+                setMode(IManipulator.KEYHOOK_DISABLED_MODE);
+                Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+                // String oldText = item.getUIString();
+                // AltInputDialog dialog = new AltInputDialog(shell, oldText.length() > 0 ? oldText : "(no ALT text)");
+                AltInputDialog dialog = new AltInputDialog(shell, item.getUIString());
+
+                if (dialog.open() == InputDialog.OK) {
+                    String newText = dialog.getResult();
+                    if (newText != null) {
+                        IMetaDataModifier modifier = new MetaDataModifier();
+                        modifier.setGenerator(new AltTextEditor());
+                        modifier.setSite(observer.getTargetFilter());
+                        modifier.setPageTitle(PlatformUIUtil.getActiveEditor().getTitle());
+                        modifier.setItem(item);
+                        modifier.setText(newText);
+                        IUserInfoGenerator.Result result = modifier.commit(preferenceStore
+                                .getBoolean(UserInfoPreferenceConstants.AUTO_SAVE));
+//                        if (result != IUserInfoGenerator.Result.NOTHING) {
+                            if (preferenceStore.getBoolean(UserInfoPreferenceConstants.AUTO_REFRESH))
+                                refresh();
+                            speak(getMessageFormatter().mes(modifier.toString(result),
+                                                       newText),
+                                  true, false);
+//                        }
+                    }
+                }
+
+                setMode(IManipulator.TREE_NAVIGATION_MODE);
+            } else {
+                speakWithFormat("Navigator.ITEM_IS_NOT_AVAILABLE", true);
+            }
+        } catch (TreeManagerException e) {
+            e.printStackTrace();
+        } catch (XMLStoreException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void speakUnavailableUserInfo() {
+        speakWithFormat("Navigator.ANNOTATION_IS_NOT_AVAILABLE", true);
+    }
+
+    public void makeLandmark() {
+        if (getCurrentEntry() != null && !getCurrentEntry().isUserEntry()) {
+            speakUnavailableUserInfo();
+            return;
+        }
+        try {
+            ITreeItem item = getTreeManager().getActiveItem();
+            IMetaDataModifier modifier = new MetaDataModifier();
+            modifier.setGenerator(getHeadingLevelOf(item) > 0 ? new HeadingCanceller() : new LandmarkMaker());
+            //          modifier.setGenerator(Vocabulary.isHeading().eval(item) ? new HeadingCanceller() : new LandmarkMaker());
+            modifier.setSite(observer.getTargetFilter());
+            modifier.setPageTitle(PlatformUIUtil.getActiveEditor().getTitle());
+            modifier.setItem(item);
+            IUserInfoGenerator.Result result = modifier.commit(preferenceStore
+                                                               .getBoolean(UserInfoPreferenceConstants.AUTO_SAVE));
+            if (preferenceStore.getBoolean(UserInfoPreferenceConstants.AUTO_REFRESH))
+                refresh();
+
+            speak(getMessageFormatter().mes(modifier.toString(result),
+                                       contentShortener.getSummary(item, true)),
+                  true, false);
+        } catch (TreeManagerException e) {
+            e.printStackTrace();
+        } catch (XMLStoreException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private short getHeadingLevelOf(ITreeItem item) {
+        short level = 0;
+        for (ITreeItem i = item; i != null; i = i.getParent()) {
+            short k = i.getHeadingLevel();
+            if (k < 0)
+                return k;
+            if (k > 0)
+                level = k;
+        }
+        return level;
+    }
+
+    private void refresh() throws TreeManagerException {
+        selectUserNVM3();
+    }
+    
+    public void removeUserInfo() {
+        if (getCurrentEntry() == null) {
+            speakWithFormat("Navigator.NO_ANNOTATION");
+            return;
+        }
+        
+        try {
+            Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+
+            String title = getMessageFormatter().mes("Navigator.USER_INFO_REMOVE_CONFIRM");
+            String message = getMessageFormatter().mes("Navigator.USER_INFO_REMOVE_MESSAGE");
+            boolean ret = MessageDialog.openQuestion(shell, title, message);
+            if (!ret)
+                return;
+            
+            IMetaDataModifier modifier = new MetaDataModifier();
+            modifier.setSite(observer.getTargetFilter());
+            modifier.setPageTitle(PlatformUIUtil.getActiveEditor().getTitle());
+            if (modifier.remove()) {
+                speakWithFormat("Navigator.ANNOTATION_IS_REMOVED", true);
+                forceRestart(false);
+            } else {
+                speakWithFormat("Navigator.NO_ANNOTATION", true);
+            }
+        } catch (XMLStoreException e) {
+            e.printStackTrace();
+        }
+    }
+    
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/SearchDialog.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/SearchDialog.java
new file mode 100644
index 0000000..9c70d95
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/SearchDialog.java
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.impl;
+
+import org.eclipse.actf.ai.navigator.Messages;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+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.Shell;
+import org.eclipse.swt.widgets.Text;
+
+
+
+
+public class SearchDialog extends Dialog {
+    private static String formInputString = "";
+
+    private Text searchField;
+    
+    private Button forwardRadio; 
+    
+    private Button backwardRadio; 
+    
+    private Button exactButton;
+    
+    private static boolean isForward = true;
+    
+    private static boolean exact = false;
+    
+    @Override
+    protected void configureShell(Shell newShell) {
+        super.configureShell(newShell);
+
+        newShell.setText(Messages.getString("FormInputDialog.Search"));
+    }
+
+    @Override
+    protected Control createDialogArea(Composite parent) {
+        Composite container = (Composite) super.createDialogArea(parent);
+        GridData gd;
+        searchField = new Text(container, SWT.BORDER);
+        gd = new GridData(GridData.FILL_HORIZONTAL);
+        gd.widthHint = 400;
+        gd.horizontalSpan = ((GridLayout) parent.getLayout()).numColumns;
+        searchField.setLayoutData(gd);
+        searchField.setText(formInputString);
+        searchField.addModifyListener(new ModifyListener() {
+            public void modifyText(ModifyEvent e) {
+                formInputString = searchField.getText();
+            }
+        });
+        
+        backwardRadio = new Button(container, SWT.RADIO);
+        backwardRadio.setSelection(!isForward);
+        backwardRadio.setText(Messages.getString("FormInputDialog.Backward"));
+        backwardRadio.addSelectionListener(new SelectionListener(){
+            public void widgetDefaultSelected(SelectionEvent e) {
+            }
+            public void widgetSelected(SelectionEvent e) {
+                isForward = false;
+            }
+        });
+        
+        forwardRadio = new Button(container, SWT.RADIO);
+        forwardRadio.setText(Messages.getString("FormInputDialog.Forward"));
+        forwardRadio.setSelection(isForward);
+        forwardRadio.addSelectionListener(new SelectionListener(){
+            public void widgetDefaultSelected(SelectionEvent e) {
+            }
+            public void widgetSelected(SelectionEvent e) {
+                isForward = true;
+            }
+        });
+
+        exactButton = new Button(container, SWT.CHECK);
+        exactButton.setSelection(exact);
+        exactButton.setText(Messages.getString("FormInputDialog.Exact"));
+        exactButton.addSelectionListener(new SelectionListener(){
+            public void widgetDefaultSelected(SelectionEvent e) {
+            }
+            public void widgetSelected(SelectionEvent e) {
+                exact = exactButton.getSelection();
+            }
+        });
+        searchField.setFocus();
+        searchField.setSelection(0, formInputString.length());
+        
+        return container;
+    }
+
+    String getString() {
+        return formInputString;
+    }
+    
+    boolean isForward(){
+        return isForward;
+    }
+    
+    boolean isExact(){
+        return exact;
+    }
+
+    SearchDialog(Shell parent) {
+        super(parent);
+    }
+
+    public void setForward(boolean b) {
+        isForward = b;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/TripJournal.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/TripJournal.java
new file mode 100644
index 0000000..fa09c3f
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/TripJournal.java
@@ -0,0 +1,250 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.impl;
+
+import org.eclipse.actf.ai.fennec.treemanager.ILocation;
+import org.eclipse.actf.ai.navigator.impl.NavigatorImpl.FocusTabResult;
+import org.eclipse.actf.ai.navigator.voice.VoiceManager;
+
+
+
+
+class TripJournal {
+    private static final boolean DEBUG = false;
+    private static final int JOURNAL_SIZE = 1024;
+
+    private static final VoiceManager voice = new VoiceManager(null);
+
+    private static class TripJournalItem {
+        final NavigatorImpl navigator;
+        final ILocation location;
+        final String url;
+
+        TripJournalItem(NavigatorImpl navigator,
+                        ILocation location,
+                        String url) {
+            this.navigator = navigator;
+            this.location = location;
+            this.url = url;
+        }
+    }
+
+    private int startIdx = 0;
+    private int endIdx = 0;
+    private int currentIdx = 0;
+    private int recordIdx = 0;
+    private TripJournalItem[] tripJournalItems = new TripJournalItem[JOURNAL_SIZE];
+
+    private boolean backwarding;
+    private boolean forwarding;
+    
+
+    private int incrementIdx(int idx) {
+        idx++;
+        if (idx == tripJournalItems.length) {
+            idx = 0;
+        }
+        return idx;
+    }
+
+    private int decrementIdx(int idx) {
+        if (idx == 0) {
+            idx = tripJournalItems.length;
+        }
+        return --idx;
+    }
+
+    private void recordInPlay(NavigatorImpl navigator,
+                              ILocation location,
+                              String url) {
+        TripJournalItem tjiOld = tripJournalItems[recordIdx];
+        if ((location == null) && (tjiOld != null)) {
+            location = tjiOld.location;
+        }
+        TripJournalItem tji = new TripJournalItem(navigator, location, url);
+        tripJournalItems[recordIdx] = tji;
+    }
+
+    public void recordJournal(NavigatorImpl navigator,
+                              ILocation location,
+                              String url,
+                              boolean init) {
+        if (init) {
+            if (DEBUG) System.err.println("Record-I");
+            currentIdx = incrementIdx(currentIdx);
+            endIdx = currentIdx;
+            tripJournalItems[currentIdx] = new TripJournalItem(navigator, location, url);
+            return;
+        }
+
+        if (!navigator.isFocused()) {
+            if (DEBUG) System.err.println("Not Recorded (out of focus)");
+            return;
+        }
+
+        if (backwarding) {
+            if (DEBUG) System.err.println("Record-B!");
+            recordInPlay(navigator, location, url);
+            backwarding = false;
+        } else if (forwarding) {
+            if (DEBUG) System.err.println("Record-F!");
+            recordInPlay(navigator, location, url);
+            forwarding = false;
+        } else {
+            if (DEBUG) System.err.println("Record!:" + url);
+            TripJournalItem tjiOld = tripJournalItems[currentIdx];
+            if (tjiOld != null) {
+                if (DEBUG) System.err.println("Old-URL:" + tjiOld.url);
+                if (url != null) {
+                    if (url.equals(tjiOld.url)) return;
+                }
+            }
+            TripJournalItem tji = new TripJournalItem(navigator, location, url);
+            tripJournalItems[currentIdx] = tji;
+            currentIdx = incrementIdx(currentIdx);
+            endIdx = currentIdx;
+            if (startIdx == endIdx) {
+                startIdx = incrementIdx(startIdx);
+            }
+        }
+    }
+
+    TripJournal() {
+    }
+
+    // --------------------------------------------------------------------------------
+    //  Browser Control
+    // --------------------------------------------------------------------------------
+
+    private void sayForward() {
+        voice.speakWithFormat("TripJournal.FORWARD", true, false);
+    }
+
+    private void sayForwardFailed() {
+        voice.speakWithFormat("TripJournal.FAILEDTOFORWARD", true, false);
+    }
+
+    public void forward(NavigatorImpl currentNavigartor) {
+        //if (backwarding || forwarding) return;
+        recordIdx = currentIdx;
+        int idxFwd = currentIdx;
+        for (;;) {
+            if (idxFwd == endIdx) {
+                sayForwardFailed();
+                return;
+            }
+            idxFwd = incrementIdx(idxFwd);
+            TripJournalItem tji = tripJournalItems[idxFwd];
+            if (tji == null) {
+                // This must not happen, but navigateComplete failed to record a trip log
+                // and the entry may be possibly missing.  So try to emulate the forwarding.
+                forwarding = true;
+                currentIdx = idxFwd;
+                currentNavigartor.goForward();
+                currentNavigartor.restoreLocation(null);
+                return;
+            }
+            switch (tji.navigator.focusTab()) {
+            case STAY:
+                // Notice that it will invoke recordJournal method;
+                forwarding = true;
+                currentIdx = idxFwd;
+                tji.navigator.goForward();
+                tji.navigator.restoreLocation(tji.location);
+                sayForward();
+                return;
+            case CHANGED:
+                currentIdx = idxFwd;
+                sayForward();
+                return;
+            case NOTFOUND:
+                break;
+            }
+        }
+    }
+
+    private void sayBackward() {
+        voice.speakWithFormat("TripJournal.BACKWARD", true, false);
+    }
+
+    private void sayBackwardFailed() {
+        voice.speakWithFormat("TripJournal.FAILEDTOBACKWARD", true, false);
+    }
+
+    public void backward(NavigatorImpl currentNavigator) {
+        //if (backwarding || forwarding) return;
+        recordIdx = currentIdx;
+        int idxBack = currentIdx;
+        if (DEBUG) System.err.println("back:" + currentIdx);
+        for (;;) {
+            if (idxBack == startIdx) {
+                sayBackwardFailed();
+                return;
+            }
+            idxBack = decrementIdx(idxBack);
+            TripJournalItem tji = tripJournalItems[idxBack];
+            if (tji == null) {
+                // Since the item was not recorded, focus the previous item.
+                if (idxBack == startIdx) {
+                    sayBackwardFailed();
+                    return;
+                }
+                currentIdx = idxBack;
+                idxBack = decrementIdx(idxBack);
+                tji = tripJournalItems[idxBack];
+                if (tji != null) {
+                    tji.navigator.focusTab();
+                }
+                sayBackward();
+                return;
+            }
+            switch (tji.navigator.focusTab()) {
+            case STAY:
+                // Notice that it will invoke recordJournal method;
+                backwarding = true;
+                currentIdx = idxBack;
+                tji.navigator.goBackward();
+                tji.navigator.restoreLocation(tji.location);
+                sayBackward();
+                return;
+            case CHANGED:
+                if (currentNavigator == null) {
+                    endIdx = idxBack;
+                } else {
+                    ILocation location = currentNavigator.getLocation();
+                    int idx = incrementIdx(idxBack);
+                    tripJournalItems[idx] = new TripJournalItem(currentNavigator,
+                                                                location,
+                                                                currentNavigator.getCurrentURL());
+                } 
+                currentIdx = idxBack;
+                sayBackward();
+                return;
+            case NOTFOUND:
+                break;
+            }
+        }
+    }
+
+    public void tripEnd() {
+        forwarding = false;
+        backwarding = false;
+    }
+
+    public void refreshEnd() {
+        TripJournalItem tji = tripJournalItems[currentIdx];
+        if (tji == null) return;
+        if (tji.navigator.focusTab() == FocusTabResult.STAY) {
+            tji.navigator.restoreLocation(tji.location);
+        }
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/WebEventListener.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/WebEventListener.java
new file mode 100644
index 0000000..889a943
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/impl/WebEventListener.java
@@ -0,0 +1,345 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.navigator.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.HashMap;
+
+import org.eclipse.actf.ai.fennec.INVM3Mediator;
+import org.eclipse.actf.ai.fennec.NVM3Plugin;
+import org.eclipse.actf.ai.fennec.treemanager.ILocation;
+import org.eclipse.actf.ai.navigator.NavigatorPlugin;
+import org.eclipse.actf.ai.navigator.broker.RequestBroker;
+import org.eclipse.actf.ai.navigator.extension.ManipulatorExtension;
+import org.eclipse.actf.ai.navigator.extension.MediaControlExtension;
+import org.eclipse.actf.ai.navigator.extension.ScreenReaderExtension;
+import org.eclipse.actf.model.IModelServiceHolder;
+import org.eclipse.actf.model.IWebBrowserACTF;
+import org.eclipse.actf.model.events.IWebBrowserACTFEventListener;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+
+public class WebEventListener implements IWebBrowserACTFEventListener {
+
+    private static final String ABOUT_BLANK_URL = "about:blank";
+
+    private NVM3Plugin getNVM3Plugin() {
+        return NVM3Plugin.getDefault();
+    }
+
+    // --------------------------------------------------------------------------------
+    //  Browser State: It keeps the state of the browser by receiving Web Browser
+    //  Events.  In overall, we should encapsulate the stateful information around the browser
+    //  into this class.
+    // --------------------------------------------------------------------------------
+    public class BrowserState {
+        public static final int STATE_NONE = -1;
+
+        public static final int STATE_UNINIT = 0;
+
+        public static final int STATE_PROGRESS = 1;
+
+        public static final int STATE_NAVIGATECOMPLETED = 3;
+
+        public static final int STATE_STARTED = 10;
+
+        boolean shouldRecord;
+        boolean initFlag;
+        ILocation savedLocation;
+        ILocation savedLocationForMyRefresh;
+
+        NavigatorImpl navigator;
+
+        public NavigatorImpl getNavigator() {
+            return navigator;
+        }
+
+        int state;
+
+
+        public int getState() {
+            return state;
+        }
+
+        void resetState() {
+            this.state = STATE_UNINIT;
+        }
+
+        void setNoState() {
+            this.state = STATE_NONE;
+        }
+
+        void forwardState(int newst) {
+            if (this.state < newst) this.state = newst;
+        }
+
+        BrowserState(IWebBrowserACTF webBrowser) {
+            this.navigator = new NavigatorImplEx(WebEventListener.this, webBrowser, 30, 1000);
+            NavigatorPlugin.getDefault().setNavigatorUI(this.navigator);
+            this.state = STATE_NONE;
+        }
+    }
+
+    private HashMap<IWebBrowserACTF, BrowserState> browserStateMap;
+
+    private RequestBroker requestBroker;
+    
+    private final TripJournal tripJournal;
+
+    private final BrowserControlImpl browserControl;
+
+    public WebEventListener() {
+        this.browserStateMap = new HashMap<IWebBrowserACTF, BrowserState>();
+        this.tripJournal = new TripJournal();
+        this.browserControl = new BrowserControlImpl(this, this.tripJournal);
+        IWebBrowserACTF.WebBrowserNavigationEventListnerHolder.LISTENER = this.browserControl;
+        ManipulatorExtension.setBrowserControl(this.browserControl);
+
+        requestBroker = new RequestBroker(this);
+    }
+    
+    public synchronized BrowserState getBrowserState(IWebBrowserACTF webBrowser) {
+        BrowserState bs = browserStateMap.get(webBrowser);
+        if (bs == null) {
+            bs = new BrowserState(webBrowser);
+            browserStateMap.put(webBrowser, bs);
+        }
+        return bs;
+    }
+
+    public void forceRestart(IWebBrowserACTF webBrowser) {
+        startNavigation(webBrowser, false);
+    }
+
+    public NavigatorImpl getFocused() {
+        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+        IWorkbenchPage page = window.getActivePage();
+        IEditorPart editor = page.getActiveEditor();
+
+        if (editor == null) return null;
+        IModelServiceHolder modelServiceHolder = (IModelServiceHolder) editor;
+        IWebBrowserACTF wb = (IWebBrowserACTF) modelServiceHolder.getModelService();
+        BrowserState bs = browserStateMap.get(wb);
+        if (bs == null) return null;
+
+        return bs.getNavigator();
+    }
+
+    // --------------------------------------------------------------------------------
+    //  Event Handlers
+    // --------------------------------------------------------------------------------
+    
+    private static final boolean EVENT_DEBUG = false;
+    
+    private void startNavigation(IWebBrowserACTF webBrowser) {
+        startNavigation(webBrowser, false);
+    }
+
+    private void startNavigation(IWebBrowserACTF webBrowser, boolean isRefresh) {
+        tripJournal.tripEnd();
+        BrowserState bs = getBrowserState(webBrowser);
+        if (bs.state >= BrowserState.STATE_STARTED)
+            return;
+        
+        if (EVENT_DEBUG) System.err.println(webBrowser.getURL() + ", isRefresh=" + isRefresh + ", " + webBrowser);
+        
+        if (bs.state == BrowserState.STATE_STARTED) {
+            MediaControlExtension.doDispose(bs.navigator.getMediaControlHandle());
+        }
+        MediaControlExtension.start(bs.navigator.getMediaControlHandle());
+
+        INVM3Mediator mediator = getNVM3Plugin().newNVM3Mediator(webBrowser);
+        bs.navigator.setNVM3Mediator(mediator);
+        bs.navigator.startNavigation(webBrowser, !isRefresh);
+        requestBroker.setNavigator(bs.navigator, webBrowser);
+
+        bs.forwardState(BrowserState.STATE_STARTED);
+        requestBroker.newPageReady();
+    }
+
+    private static final String FILE_SCHEME_SUFFIX = "file:///";
+    private String unifyURLFileScheme(String url) {
+        if (url.startsWith(FILE_SCHEME_SUFFIX)) {
+            url = url.substring(FILE_SCHEME_SUFFIX.length());
+            try {
+                url = URLDecoder.decode(url, "UTF-8");
+            } catch (UnsupportedEncodingException e) {
+                e.printStackTrace();
+            }
+            return url.replace('/', '\\');
+        }
+        return url;
+    }
+
+    private boolean urlEquals(String url1, String url2) {
+        if ((url1 == null) || (url2 == null)) return false;
+        return unifyURLFileScheme(url1).equals(unifyURLFileScheme(url2));
+    }
+
+    public void navigateComplete(IWebBrowserACTF webBrowser, String url) { 
+        if (EVENT_DEBUG) System.out.println("navigateComplete " + url + ", "
+                                            + webBrowser.getURL() + " | " + webBrowser);
+        
+        if (!urlEquals(url, webBrowser.getURL())) return;
+
+        BrowserState bs = getBrowserState(webBrowser);
+        if ((bs.shouldRecord)
+            && (bs.state < BrowserState.STATE_NAVIGATECOMPLETED)) {
+            tripJournal.recordJournal(bs.navigator,
+                                      bs.savedLocation,
+                                      webBrowser.getURL(),
+                                      bs.initFlag);
+        }
+        bs.forwardState(BrowserState.STATE_NAVIGATECOMPLETED);
+        bs.navigator.navigateComplete();
+    }
+
+    public void titleChange(IWebBrowserACTF webBrowser, String title) {
+        if (EVENT_DEBUG) System.out.println("titleChange " + title + ", "+ webBrowser);
+
+        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+        IWorkbenchPage page = window.getActivePage();
+
+        IEditorPart editor = page.getActiveEditor();
+        if (editor instanceof IModelServiceHolder) {
+            if (webBrowser != ((IModelServiceHolder) editor).getModelService()) {
+                return;
+            }
+        }
+
+        setWindowTitle(title);
+    }
+
+    public void dispose() {
+    }
+
+    public void progressChange(IWebBrowserACTF webBrowser, int progress, int progressMax) {
+        if (EVENT_DEBUG) System.out.println("progressChange progress=" + progress + ", progressMax=" + progressMax + ", " + webBrowser);
+        BrowserState bs = getBrowserState(webBrowser);
+        bs.forwardState(BrowserState.STATE_PROGRESS);
+        bs.navigator.progressChange(progress, progressMax);
+    }
+
+    public void myDocumentComplete(IWebBrowserACTF webBrowser) {
+        if (EVENT_DEBUG) System.out.println("myDocumentComplete " + webBrowser);
+        BrowserState bs = getBrowserState(webBrowser);
+        
+        if (!(ABOUT_BLANK_URL.equals(webBrowser.getURL()))) {
+            bs.navigator.speakTab(false);
+            startNavigation(webBrowser);
+        } else {
+            bs.setNoState();
+        }
+    }
+
+    public void focusChange(IWebBrowserACTF webBrowser) {
+        if (EVENT_DEBUG) System.out.println("focusChange " + webBrowser);
+        BrowserState bs = getBrowserState(webBrowser);
+        ManipulatorExtension.setNavigator(bs.navigator);
+        requestBroker.setNavigator(bs.navigator, webBrowser);
+        MediaControlExtension.start(bs.navigator.getMediaControlHandle());
+        setWindowTitle();
+    }
+
+    private void setWindowTitle(String title) {
+        String productName = Platform.getProduct().getName();
+        PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell().setText(title + " - " + productName);
+    }
+
+    private void setWindowTitle() {
+        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+        IWorkbenchPage page = window.getActivePage();
+        String title = page.getActiveEditor().getTitle();
+        setWindowTitle(title);
+    }
+
+    public void beforeNavigate(IWebBrowserACTF webBrowser,
+                               String url,
+                               String targetFrameName,
+                               boolean isInNavigation) {
+        if (EVENT_DEBUG) System.out.println("beforeNavigate " + url + ", " + targetFrameName + ", isInNavigation=" + isInNavigation);
+        BrowserState bs = getBrowserState(webBrowser);
+        //if (isInNavigation) {
+        if (!url.startsWith("javascript")) {
+            if (!(ABOUT_BLANK_URL.equals(url))) {
+                if (bs.state == BrowserState.STATE_NONE) {
+                    bs.initFlag = true;
+                } else {
+                    bs.initFlag = false;
+                }
+                bs.shouldRecord = true;
+                bs.savedLocation = bs.navigator.getLocation();
+            } else {
+                bs.shouldRecord = false;
+            }
+            bs.resetState();
+            bs.navigator.beforeNavigation(webBrowser.getURL());
+        } else {
+            bs.shouldRecord = false;
+        }
+        ScreenReaderExtension.takeBackControl(webBrowser);
+    }
+
+    public void browserDisposed(IWebBrowserACTF webBrowser, String title) {
+        if (EVENT_DEBUG) System.out.println("browserDisposed " + title);
+        BrowserState bs = getBrowserState(webBrowser);
+        bs.navigator.speakCloseTab(title);
+
+        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+        IWorkbenchPage page = window.getActivePage();
+        if ((page == null) || (page.getActiveEditor() == null)) {
+            setWindowTitle("No Tab");
+        }
+    }
+
+    public void myRefresh(IWebBrowserACTF webBrowser) {
+        if (EVENT_DEBUG) System.out.println("myRefresh " + webBrowser);
+        BrowserState bs = getBrowserState(webBrowser);
+        bs.resetState();
+        bs.savedLocationForMyRefresh = bs.navigator.getLocation();
+        bs.navigator.beforeNavigation(webBrowser.getURL());
+    }
+
+    public void myRefreshComplete(IWebBrowserACTF webBrowser) {
+        if (EVENT_DEBUG) System.out.println("Refresh Complete " + webBrowser);
+        BrowserState bs = getBrowserState(webBrowser);
+        bs.navigator.restoreLocation(bs.savedLocationForMyRefresh);
+        startNavigation(webBrowser, true);
+    }
+
+    public void navigateStop(IWebBrowserACTF webBrowser) {
+        if (EVENT_DEBUG) System.out.println("navigateStop " + webBrowser);
+    }
+
+    public void focusGainedOfAddressText(IWebBrowserACTF webBrowser) {
+        if (EVENT_DEBUG) System.out.println("focusGainedOfAddressText " + webBrowser);
+        BrowserState bs = getBrowserState(webBrowser);
+        bs.navigator.enterBrowserAddress();
+    }
+
+    public void focusLostOfAddressText(IWebBrowserACTF webBrowser) {
+        if (EVENT_DEBUG) System.out.println("focusLostOfAddressText " + webBrowser);
+        BrowserState bs = getBrowserState(webBrowser);
+        bs.navigator.exitFormMode();
+    }
+
+    public void newWindow(IWebBrowserACTF webBrowser) {
+        if (EVENT_DEBUG) System.out.println("newWindow " + webBrowser);
+        BrowserState bs = getBrowserState(webBrowser);
+        bs.navigator.speakOpenTab();
+    }
+
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/message/Speech.properties b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/message/Speech.properties
new file mode 100644
index 0000000..1e5db03
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/message/Speech.properties
@@ -0,0 +1,179 @@
+###############################################################################
+# Copyright (c) 2007 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     Hisashi MIYASHITA, Daisuke SATO - initial configuration.
+###############################################################################
+
+Navigator.LINK=Link
+Navigator.VISITED_LINK=Visited link
+Navigator.HEADING=Heading
+Navigator.LIST_TOP=List of {0} items
+Navigator.BUTTON=Button
+Navigator.TEXTBOX=Edit
+Navigator.TEXTAREA=Multiline edit contains text
+Navigator.TEXTAREA_EMPTY=Multiline edit
+Navigator.PASSWORD=password edit
+Navigator.FILE_UPLOAD=File upload edit
+Navigator.CHECKED=Checked
+Navigator.NOT_CHECKED=Not checked
+Navigator.CHECKBOX=Check box
+Navigator.RADIO=Radio button
+Navigator.COMBO_BOX=Combo box
+Navigator.COMBO_BOX_NOSELECTION=no selection
+
+Navigator.MEDIA=Multimedia
+
+Navigator.PASSWORD_STAR3=star star star
+Navigator.WRAPPING_TO_TOP=wrapping to top
+Navigator.WRAPPING_TO_BOTTOM=wrapping to top
+
+Navigator.NO_HEADING=No heading
+Navigator.NO_LISTITEM=No list item
+Navigator.NO_LINK=No link
+Navigator.NO_INPUT=No input
+Navigator.NO_BLOCK=No block
+Navigator.NO_OBJECT=No object
+Navigator.NO_SELECTION=No selection
+Navigator.NO_ACCESSKEY=No access key {0}
+Navigator.NO_ALTALABLE=No item needs alt text
+Navigator.NO_MEDIA=No multimedia object
+
+Navigator.MSAA_FLASH_CONTENT=Flash Content
+Navigator.FLASH_CONTENT=Windowless Flash Content
+Navigator.FLASH_END=Flash Content End
+
+Navigator.VIEWS_ARE_OPEND=Tree view is opend
+Navigator.VIEWS_ARE_CLOSED=Tree view is closed
+
+Navigator.LAUNCH_DEFAULT_BROWSER=Launch Default Browser. {0}
+Navigator.CLOSE_TAB={0} is closed.
+Navigator.NEW_TAB=New tab is opend.
+Navigator.MOVE_TAB={0} {1} of {2} tabs
+
+Navigator.ACCESSKEY_GUIDE=Access key is Alt Shift {0}
+
+Navigator.REFRESH=Refresh Page
+
+Navigator.PROGRESS=Loading {0} percent.
+Navigator.STARTNAVIGATION=Page is loaded.
+Navigator.FENNEC_NAME=Fennec {0} is used.
+Navigator.SUBITEMS={0} sub-items
+Navigator.N_OF_M={0} of {1}
+Navigator.NO_SUBITEMS=No sub-items
+Navigator.NO_CONTROLS=No controls
+Navigator.NOT_AVAILABLE={0} is not available
+Navigator.TOP=Top item
+Navigator.BOTTOM=Last item
+Navigator.INPUTABLE=Inputable
+Navigator.LEVEL=Level {0}
+Navigator.TOP_LEVEL=Level zero
+Navigator.CLICK=Click
+Navigator.PLAY_TIME=Play time {0}
+Navigator.HH_MM_SS={2} hour {1} minutes {0} seconds
+Navigator.MM_SS={1} minutes {0} seconds
+Navigator.SS={0} seconds
+Navigator.KEYBOARD_ON=Keyboard navigation start
+Navigator.KEYBOARD_OFF=Keyboard navigation end
+Navigator.NOT_TABLE=Not in the table
+Navigator.UNDONE=work in progress
+Navigator.SPEECHSPEED=speech speed is {0}
+Navigator.SPEECHSPEEDMAX=speech speed is max
+Navigator.SPEECHSPEEDMIN=speech speed is min
+Navigator.NO_OTHER_FENNEC=Fennec meta data is not available
+Navigator.NO_FENNEC=
+Navigator.ENTERBROWSERADDRESS=Enter URI
+
+Navigator.NO_FENNEC_MESSAGE=No Fennec
+Navigator.NO_FENNEC_NAME=Anonymous
+
+Navigator.STOPMEDIA=stop
+Navigator.PLAYMEDIA=play
+Navigator.PAUSEMEDIA=pause
+Navigator.RESUMEMEDIA=resume
+Navigator.WAITINGMEDIA=waiting for media
+Navigator.FASTFORWARDMEDIA=fast forward
+Navigator.FASTREVERSEMEDIA=fast reverse
+Navigator.VOLUMEUP=volume up
+Navigator.VOLUMEDOWN=volume down
+Navigator.VOLUMEMAX=maximum volume
+Navigator.VOLUMEMIN=minimum volume
+Navigator.MUTEON=mute
+Navigator.MUTEOFF=mute off
+Navigator.CANNOT_PAUSE=can not pause
+Navigator.MOVIE_START=movie start
+
+Navigator.NOT_AVAILABEL=not available
+
+Navigator.NOVIDEO=No video
+Navigator.SINGLEVIDEO=one video
+Navigator.VIDEOCOUNT={0} videos
+Navigator.NOSOUND=No sound
+Navigator.SINGLESOUND=one sound
+Navigator.SOUNDCOUNT={0} sound
+Navigator.VIDEOINDEX={0} of {1} videos
+
+Navigator.VIDEO_AT=at {0}
+Navigator.VIDEO_TOTAL=Total length is {0}
+
+NVM3.EMPTY_PAGE=The page is empty.
+
+ContentShortener.VerbalForm={0} bla bla bla
+ContentShortener.NonVerbalForm={0} ...
+
+LandmarkMaker.Result.CREATED=a landmark was added here : {0}
+LandmarkMaker.Result.REMOVED=the heading now gets effective : {0}
+LandmarkMaker.Result.ERROR=Failed to set a landmark here : {0} 
+LandmarkMaker.Result.NOTHING=a landmark is already added here : {0}
+
+HeadingCanceller.Result.CREATED=the heading was turned off : {0}
+HeadingCanceller.Result.REMOVED=the landmark was removed : {0}
+HeadingCanceller.Result.ERROR=Failed to remove the heading : {0}
+HeadingCanceller.Result.NOTHING=the heading is already turned off : {0}
+
+AltTextEditor.Result.CREATED=an alt text was added here : {0}
+AltTextEditor.Result.REMOVED=the alt text was removed
+AltTextEditor.Result.CHANGED=the alt text was edited : {0}
+AltTextEditor.Result.ERROR=Failed to edit the alt text
+
+AudioDescription.on=Audio Description is on
+AudioDescription.off=Audio Description is off
+AudioDescription.available=Audio Description is available
+AudioDescription.notAvailable=No Audio Description
+AudioDescription.noExtension=No Audio Description Extension
+
+Navigator.ANNOTATION_IS_NOT_AVAILABLE=Annotation function is not available
+Navigator.ANNOTATION_IS_SAVED=Annotation is saved
+Navigator.ALT_TEXT_WERE_GUESSED={0} ALT text were guessed
+Navigator.THERE_IS_NO_MISSING_ALT_TEXT=There is no missing ALT text
+Navigator.ANNOTATION_LINK_TO=Link to {0}
+Navigator.ITEM_IS_NOT_AVAILABLE=This item is not alterable
+Navigator.ANNOTATION_IS_REMOVED=All annotations are removed
+Navigator.NO_ANNOTATION=There is no annotation
+
+Navigator.USER_INFO_REMOVE_CONFIRM=Remove All User Annotation
+Navigator.USER_INFO_REMOVE_MESSAGE=Will you remove all user annotation from this page?
+
+Navigator.THERE_ARE_NO_TAB=There are no tabs, please use Control O.
+
+TripJournal.FORWARD=Forward
+TripJournal.FAILEDTOFORWARD=Cannot forward the page.
+TripJournal.BACKWARD=Backward
+TripJournal.FAILEDTOBACKWARD=Cannot backward the page.
+
+Navigator.EXPORT_ANNOTATION=Export annotations of current page.
+Navigator.EXPORT_ALL_ANNOTATIONS=Export all your annotations.
+Navigator.IMPORT_FENNEC_FILE=Import annotation file.
+Navigator.EXPORT_IS_SUCCEEDED=Export is succeeded.
+Navigator.EXPORT_IS_FAILED=Export is failed.
+Navigator.IMPORT_IS_SUCCEEDED=Import is succeeded.
+Navigator.IMPORT_IS_FAILED=Import is failed.
+Navigator.OVERWRITE_CONFIRM=Exporting...
+Navigator.OVERWRITE_MESSAGE={0} already exists. Do you want to replace it?
+
+Navigator.REPAIR_START=Execute Flash repair.
+Navigator.REPAIR_FINISHED=Flash Repair is finished.
\ No newline at end of file
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/message/Speech_ja.properties b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/message/Speech_ja.properties
new file mode 100644
index 0000000..3af8e33
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/message/Speech_ja.properties
@@ -0,0 +1,179 @@
+###############################################################################
+# Copyright (c) 2007 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#     Hisashi MIYASHITA, Daisuke SATO - initial configuration.
+###############################################################################
+
+Navigator.LINK=\u30ea\u30f3\u30af
+Navigator.VISITED_LINK=\u65e2\u8aad\u30ea\u30f3\u30af
+Navigator.HEADING=\u898b\u51fa\u3057
+Navigator.LIST_TOP={0}\u9805\u76ee\u306e\u30ea\u30b9\u30c8
+Navigator.BUTTON=\u30dc\u30bf\u30f3
+Navigator.TEXTBOX=\u30c6\u30ad\u30b9\u30c8\u5165\u529b
+Navigator.TEXTAREA=\u30c6\u30ad\u30b9\u30c8\u30a8\u30ea\u30a2\u30c6\u30ad\u30b9\u30c8\u6709\u308a
+Navigator.TEXTAREA_EMPTY=\u30c6\u30ad\u30b9\u30c8\u30a8\u30ea\u30a2
+Navigator.PASSWORD=\u30d1\u30b9\u30ef\u30fc\u30c9\u5165\u529b\u30dc\u30c3\u30af\u30b9
+Navigator.FILE_UPLOAD=\u30d5\u30a1\u30a4\u30eb\u9078\u629e
+Navigator.CHECKED=\u30c1\u30a7\u30c3\u30af\u6709\u308a
+Navigator.NOT_CHECKED=\u30c1\u30a7\u30c3\u30af\u7121\u3057
+Navigator.CHECKBOX=\u30c1\u30a7\u30c3\u30af\u30dc\u30c3\u30af\u30b9
+Navigator.RADIO=\u30e9\u30b8\u30aa\u30dc\u30bf\u30f3
+Navigator.COMBO_BOX=\u30b3\u30f3\u30dc\u30dc\u30c3\u30af\u30b9
+Navigator.COMBO_BOX_NOSELECTION=\u9078\u629e\u3055\u308c\u3066\u3044\u308b\u9805\u76ee\u306f\u3042\u308a\u307e\u305b\u3093
+
+Navigator.MEDIA=\u30de\u30eb\u30c1\u30e1\u30c7\u30a3\u30a2
+
+Navigator.PASSWORD_STAR3=\u30b9\u30bf\u30fc\u30b9\u30bf\u30fc\u30b9\u30bf\u30fc
+Navigator.WRAPPING_TO_TOP=\u5148\u982d\u306b\u623b\u3063\u3066
+Navigator.WRAPPING_TO_BOTTOM=\u6700\u5f8c\u306b\u623b\u3063\u3066
+
+Navigator.NO_HEADING=\u898b\u51fa\u3057\u306f\u3042\u308a\u307e\u305b\u3093
+Navigator.NO_LISTITEM=\u30ea\u30b9\u30c8\u9805\u76ee\u306f\u3042\u308a\u307e\u305b\u3093
+Navigator.NO_LINK=\u30ea\u30f3\u30af\u306f\u3042\u308a\u307e\u305b\u3093
+Navigator.NO_INPUT=\u30d5\u30a9\u30fc\u30e0\u306f\u3042\u308a\u307e\u305b\u3093
+Navigator.NO_BLOCK=\u30d6\u30ed\u30c3\u30af\u306f\u3042\u308a\u307e\u305b\u3093
+Navigator.NO_OBJECT=\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306f\u3042\u308a\u307e\u305b\u3093
+Navigator.NO_SELECTION=\u9078\u629e\u9805\u76ee\u306f\u3042\u308a\u307e\u305b\u3093
+Navigator.NO_ACCESSKEY=\u30a2\u30af\u30bb\u30b9\u30ad\u30fc\u306f\u3042\u308a\u307e\u305b\u3093
+Navigator.NO_ALTALABLE=\u4ee3\u66ff\u30c6\u30ad\u30b9\u30c8\u304c\u5fc5\u8981\u306a\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306f\u3042\u308a\u307e\u305b\u3093
+Navigator.NO_MEDIA=\u30de\u30eb\u30c1\u30e1\u30c7\u30a3\u30a2\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306f\u3042\u308a\u307e\u305b\u3093
+
+Navigator.MSAA_FLASH_CONTENT=\u30d5\u30e9\u30c3\u30b7\u30e5\u30b3\u30f3\u30c6\u30f3\u30c4
+Navigator.FLASH_CONTENT=\u30a6\u30a3\u30f3\u30c9\u30a6\u30ec\u30b9\u30d5\u30e9\u30c3\u30b7\u30e5\u30b3\u30f3\u30c6\u30f3\u30c4
+Navigator.FLASH_END=\u30d5\u30e9\u30c3\u30b7\u30e5\u30b3\u30f3\u30c6\u30f3\u30c4\u7d42\u308f\u308a
+
+Navigator.VIEWS_ARE_OPEND=\u30c4\u30ea\u30fc\u30d3\u30e5\u30fc\u304c\u958b\u3044\u3066\u3044\u307e\u3059
+Navigator.VIEWS_ARE_CLOSED=\u30c4\u30ea\u30fc\u30d3\u30e5\u30fc\u306f\u9589\u3058\u3066\u3044\u307e\u3059
+
+Navigator.LAUNCH_DEFAULT_BROWSER=\u901a\u5e38\u306e\u30d6\u30e9\u30a6\u30b6\u3067\u958b\u304d\u307e\u3059. {0}
+Navigator.CLOSE_TAB=\u30bf\u30d6\u3092\u9589\u3058\u307e\u3057\u305f {0}
+Navigator.NEW_TAB=\u65b0\u3057\u3044\u30bf\u30d6\u304c\u958b\u304d\u307e\u3057\u305f
+Navigator.MOVE_TAB={0} \u30bf\u30d6\u306e{1}\u500b\u4e2d{2}\u500b\u76ee
+
+Navigator.ACCESSKEY_GUIDE=\u30a2\u30af\u30bb\u30b9\u30ad\u30fc\u306f\u30aa\u30eb\u30c8 \u30b7\u30d5\u30c8 {0}
+
+Navigator.REFRESH=\u6700\u65b0\u306e\u60c5\u5831\u306b\u66f4\u65b0
+
+Navigator.PROGRESS=\u30ed\u30fc\u30c9\u4e2d
+Navigator.STARTNAVIGATION=\u30ed\u30fc\u30c9\u5b8c\u4e86
+Navigator.FENNEC_NAME=\u5916\u90e8\u60c5\u5831 {0} \u3092\u9069\u7528\u3057\u307e\u3059
+Navigator.SUBITEMS={0}\u500b\u306e\u30b5\u30d6\u30a2\u30a4\u30c6\u30e0\u304c\u3042\u308a\u307e\u3059
+Navigator.N_OF_M={1}\u500b\u4e2d{0}\u500b\u76ee
+Navigator.NO_SUBITEMS=\u30b5\u30d6\u30a2\u30a4\u30c6\u30e0\u304c\u3042\u308a\u307e\u305b\u3093
+Navigator.NO_CONTROLS=\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u304c\u3042\u308a\u307e\u305b\u3093
+Navigator.NOT_AVAILABLE={0}\u306f\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093
+Navigator.TOP=\u5148\u982d\u3067\u3059
+Navigator.BOTTOM=\u6700\u5f8c\u3067\u3059
+Navigator.INPUTABLE=\u5165\u529b\u53ef\u80fd
+Navigator.LEVEL=\u30ec\u30d9\u30eb{0}
+Navigator.TOP_LEVEL=\u30ec\u30d9\u30eb0
+Navigator.CLICK=\u30af\u30ea\u30c3\u30af
+Navigator.PLAY_TIME=\u518d\u751f\u6642\u9593 {0}
+Navigator.HH_MM_SS={2}\u6642\u9593 {1}\u5206 {0}\u79d2
+Navigator.MM_SS={1}\u5206 {0}\u79d2
+Navigator.SS={0}\u79d2
+Navigator.KEYBOARD_ON=\u30ad\u30fc\u30dc\u30fc\u30c9\u4f7f\u7528\u958b\u59cb
+Navigator.KEYBOARD_OFF=\u30ad\u30fc\u30dc\u30fc\u30c9\u4f7f\u7528\u7d42\u4e86
+Navigator.NOT_TABLE=\u30c6\u30fc\u30d6\u30eb\u306e\u5916\u3067\u3059
+Navigator.UNDONE=\u51e6\u7406\u4e2d\u3067\u3059
+Navigator.SPEECHSPEED=\u30b9\u30d4\u30fc\u30c9{0}
+Navigator.SPEECHSPEEDMAX=\u6700\u9ad8\u30b9\u30d4\u30fc\u30c9\u3067\u3059
+Navigator.SPEECHSPEEDMIN=\u6700\u4f4e\u30b9\u30d4\u30fc\u30c9\u3067\u3059
+Navigator.NO_OTHER_FENNEC=\u5916\u90e8\u60c5\u5831\u3092\u5207\u308a\u66ff\u3048\u3089\u308c\u307e\u305b\u3093
+Navigator.NO_FENNEC=\u9069\u7528\u53ef\u80fd\u306a\u5916\u90e8\u60c5\u5831\u304c\u3042\u308a\u307e\u305b\u3093
+Navigator.ENTERBROWSERADDRESS=URI\u5165\u529b
+
+Navigator.NO_FENNEC_MESSAGE=Fennec\u7121\u52b9
+Navigator.NO_FENNEC_NAME=\u7121\u540d
+
+Navigator.STOPMEDIA=\u505c\u6b62
+Navigator.PLAYMEDIA=\u518d\u751f
+Navigator.PAUSEMEDIA=\u4e2d\u65ad
+Navigator.RESUMEMEDIA=\u518d\u958b
+Navigator.WAITINGMEDIA=\u5f85\u6a5f\u4e2d
+Navigator.FASTFORWARDMEDIA=\u65e9\u9001\u308a
+Navigator.FASTREVERSEMEDIA=\u5dfb\u304d\u623b\u3057
+Navigator.VOLUMEUP=\u97f3\u91cf\u5927
+Navigator.VOLUMEDOWN=\u97f3\u91cf\u5c0f
+Navigator.VOLUMEMAX=\u6700\u5927\u97f3\u91cf
+Navigator.VOLUMEMIN=\u97f3\u91cf\u7121\u3057
+Navigator.MUTEON=\u30df\u30e5\u30fc\u30c8
+Navigator.MUTEOFF=\u30df\u30e5\u30fc\u30c8\u30aa\u30d5
+Navigator.CANNOT_PAUSE=\u4e2d\u65ad\u3067\u304d\u307e\u305b\u3093
+Navigator.MOVIE_START=\u518d\u751f\u958b\u59cb
+
+Navigator.NOT_AVAILABEL=\u5229\u7528\u3067\u304d\u307e\u305b\u3093
+
+Navigator.NOVIDEO=\u52d5\u753b\u306f\u3042\u308a\u307e\u305b\u3093
+Navigator.SINGLEVIDEO=\u3072\u3068\u3064\u306e\u52d5\u753b
+Navigator.VIDEOCOUNT={0}\u500b\u306e\u52d5\u753b
+Navigator.NOSOUND=\u97f3\u6e90\u306f\u3042\u308a\u307e\u305b\u3093
+Navigator.SINGLESOUND=\u3072\u3068\u3064\u306e\u97f3\u6e90
+Navigator.SOUNDCOUNT={0}\u500b\u306e\u97f3\u6e90
+Navigator.VIDEOINDEX={1}\u500b\u4e2d{0}\u500b\u76ee\u306e\u30d3\u30c7\u30aa
+
+Navigator.VIDEO_AT={0}\u3092\u518d\u751f\u4e2d
+Navigator.VIDEO_TOTAL=\u5168\u4f53\u3067{0}
+
+NVM3.EMPTY_PAGE=\u304b\u3089\u306e\u30da\u30fc\u30b8\u3067\u3059\u3002
+
+ContentShortener.VerbalForm={0} \u4ee5\u4e0b\u7565
+ContentShortener.NonVerbalForm={0} ...
+
+LandmarkMaker.Result.CREATED=\u3057\u304a\u308a\u304c{0}\u306b\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f
+LandmarkMaker.Result.REMOVED=\u7121\u52b9\u306b\u306a\u3063\u3066\u3044\u305f\u898b\u51fa\u3057\u304c\u6709\u52b9\u306b\u306a\u308a\u307e\u3057\u305f : {0}
+LandmarkMaker.Result.ERROR=\u3057\u304a\u308a\u3092{0}\u306b\u8a2d\u5b9a\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f
+LandmarkMaker.Result.NOTHING=\u3057\u304a\u308a\u306f\u3059\u3067\u306b{0}\u306b\u3042\u308a\u307e\u3059
+
+HeadingCanceller.Result.CREATED=\u898b\u51fa\u3057\u3092\u7121\u52b9\u5316\u3057\u307e\u3057\u305f : {0}
+HeadingCanceller.Result.REMOVED=\u3057\u304a\u308a\u304c{0}\u304b\u3089\u53d6\u308a\u9664\u304b\u308c\u307e\u3057\u305f
+HeadingCanceller.Result.ERROR=\u898b\u51fa\u3057\u3092\u53d6\u308a\u9664\u3051\u307e\u305b\u3093\u3067\u3057\u305f : {0}
+HeadingCanceller.Result.NOTHING=\u898b\u51fa\u3057\u306f\u3059\u3067\u306b\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059 : {0}
+
+AltTextEditor.Result.CREATED=\u4ee3\u66ff\u30c6\u30ad\u30b9\u30c8\u3092{0}\u3068\u8a2d\u5b9a\u3057\u307e\u3057\u305f
+AltTextEditor.Result.REMOVED=\u4ee3\u66ff\u30c6\u30ad\u30b9\u30c8\u3092\u53d6\u308a\u9664\u304d\u307e\u3057\u305f
+AltTextEditor.Result.CHANGED=\u4ee3\u66ff\u30c6\u30ad\u30b9\u30c8\u3092{0}\u306b\u5909\u66f4\u3057\u307e\u3057\u305f
+AltTextEditor.Result.ERROR=\u4ee3\u66ff\u30c6\u30ad\u30b9\u30c8\u306e\u7de8\u96c6\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+
+AudioDescription.on=\u97f3\u58f0\u89e3\u8aac\u30aa\u30f3
+AudioDescription.off=\u97f3\u58f0\u89e3\u8aac\u30aa\u30d5
+AudioDescription.available=\u97f3\u58f0\u89e3\u8aac\u6709\u308a
+AudioDescription.notAvailable=\u97f3\u58f0\u89e3\u8aac\u7121\u3057
+AudioDescription.noExtension=\u97f3\u58f0\u89e3\u8aac\u62e1\u5f35\u7121\u3057
+
+Navigator.ANNOTATION_IS_NOT_AVAILABLE=\u6ce8\u91c8\u6a5f\u80fd\u306f\u5229\u7528\u3067\u304d\u307e\u305b\u3093
+Navigator.ANNOTATION_IS_SAVED=\u6ce8\u91c8\u304c\u4fdd\u5b58\u3055\u308c\u307e\u3057\u305f
+Navigator.ALT_TEXT_WERE_GUESSED={0} \u500b\u306e\u4ee3\u66ff\u30c6\u30ad\u30b9\u30c8\u304c\u63a8\u6e2c\u3055\u308c\u307e\u3057\u305f
+Navigator.THERE_IS_NO_MISSING_ALT_TEXT=\u4ee3\u66ff\u30c6\u30ad\u30b9\u30c8\u304c\u5fc5\u8981\u306a\u9805\u76ee\u306f\u3042\u308a\u307e\u305b\u3093
+Navigator.ANNOTATION_LINK_TO={0}\u3078\u306e\u30ea\u30f3\u30af
+Navigator.ITEM_IS_NOT_AVAILABLE=\u4ee3\u66ff\u30c6\u30ad\u30b9\u30c8\u3092\u4ed8\u3051\u3089\u308c\u306a\u3044\u9805\u76ee\u3067\u3059
+Navigator.ANNOTATION_IS_REMOVED=\u6ce8\u91c8\u3092\u5168\u3066\u524a\u9664\u3057\u307e\u3057\u305f
+Navigator.NO_ANNOTATION=\u6ce8\u91c8\u304c\u3042\u308a\u307e\u305b\u3093
+
+Navigator.USER_INFO_REMOVE_CONFIRM=\u6ce8\u91c8\u3092\u5168\u3066\u524a\u9664\u3057\u307e\u3059
+Navigator.USER_INFO_REMOVE_MESSAGE=\u3053\u306e\u30da\u30fc\u30b8\u306e\u5168\u3066\u306e\u6ce8\u91c8\u306e\u60c5\u5831\u3092\u524a\u9664\u3057\u307e\u3059\u304b\uff1f
+
+Navigator.THERE_ARE_NO_TAB=\u30bf\u30d6\u304c\u3042\u308a\u307e\u305b\u3093\u3001\u30b3\u30f3\u30c8\u30ed\u30fc\u30ebO\u3092\u4f7f\u7528\u3057\u3066\u4e0b\u3055\u3044\u3002
+
+TripJournal.FORWARD=\u9032\u3080
+TripJournal.FAILEDTOFORWARD=\u6700\u5f8c\u3067\u3059
+TripJournal.BACKWARD=\u623b\u308b
+TripJournal.FAILEDTOBACKWARD=\u5148\u982d\u3067\u3059
+
+Navigator.EXPORT_ANNOTATION=\u73fe\u5728\u306e\u30da\u30fc\u30b8\u306e\u6ce8\u91c8\u3092\u66f8\u304d\u51fa\u3057\u307e\u3059
+Navigator.EXPORT_ALL_ANNOTATIONS=\u6ce8\u91c8\u30c7\u30fc\u30bf\u3092\u5168\u3066\u66f8\u304d\u51fa\u3057\u307e\u3059
+Navigator.IMPORT_FENNEC_FILE=\u6ce8\u91c8\u30c7\u30fc\u30bf\u3092\u8aad\u307f\u8fbc\u307f\u307e\u3059
+Navigator.EXPORT_IS_SUCCEEDED=\u66f8\u304d\u51fa\u3057\u304c\u5b8c\u4e86\u3057\u307e\u3057\u305f
+Navigator.EXPORT_IS_FAILED=\u66f8\u304d\u51fa\u3057\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u3042\u308a\u307e\u3057\u305f
+Navigator.IMPORT_IS_SUCCEEDED=\u8aad\u307f\u8fbc\u307f\u304c\u5b8c\u4e86\u3057\u307e\u3057\u305f
+Navigator.IMPORT_IS_FAILED=\u8aad\u307f\u8fbc\u307f\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u3042\u308a\u307e\u3057\u305f
+Navigator.OVERWRITE_CONFIRM=\u66f8\u304d\u51fa\u3057\u4e2d...
+Navigator.OVERWRITE_MESSAGE={0} \u306f\u65e2\u306b\u3042\u308a\u307e\u3059\u3002\u4e0a\u66f8\u304d\u3057\u3066\u7f6e\u304d\u63db\u3048\u307e\u3059\u304b\uff1f
+
+Navigator.REPAIR_START=Flash\u306e\u4fee\u5fa9\u3092\u958b\u59cb\u3057\u307e\u3059
+Navigator.REPAIR_FINISHED=Flash\u306e\u4fee\u5fa9\u304c\u5b8c\u4e86\u3057\u307e\u3057\u305f
\ No newline at end of file
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/preferences/UserInfoPreferenceConstants.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/preferences/UserInfoPreferenceConstants.java
new file mode 100644
index 0000000..b29b3db
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/preferences/UserInfoPreferenceConstants.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.navigator.preferences;
+
+/**
+ * Constant definitions for plug-in preferences
+ */
+public class UserInfoPreferenceConstants {
+
+	public static final String AUTO_SAVE = "autoSave";
+
+    public static final String AUTO_REFRESH = "autoRefresh";
+    
+    public static final String NONVERBAL_OVERVIEW = "nonverbalOverview";
+    
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/preferences/UserInfoPreferenceInitializer.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/preferences/UserInfoPreferenceInitializer.java
new file mode 100644
index 0000000..09f4713
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/preferences/UserInfoPreferenceInitializer.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.navigator.preferences;
+
+import org.eclipse.actf.ai.navigator.NavigatorPlugin;
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+
+/**
+ * Class used to initialize default preference values.
+ */
+public class UserInfoPreferenceInitializer extends AbstractPreferenceInitializer {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer#initializeDefaultPreferences()
+	 */
+	@Override
+    public void initializeDefaultPreferences() {
+		IPreferenceStore store = NavigatorPlugin.getDefault().getPreferenceStore();
+		store.setDefault(UserInfoPreferenceConstants.AUTO_REFRESH, true);
+        store.setDefault(UserInfoPreferenceConstants.AUTO_SAVE, true);
+        store.setDefault(UserInfoPreferenceConstants.NONVERBAL_OVERVIEW, true);
+	}
+
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/preferences/UserInfoPreferencePage.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/preferences/UserInfoPreferencePage.java
new file mode 100644
index 0000000..4fdaa94
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/preferences/UserInfoPreferencePage.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.navigator.preferences;
+
+import org.eclipse.actf.ai.navigator.Messages;
+import org.eclipse.actf.ai.navigator.NavigatorPlugin;
+import org.eclipse.jface.preference.*;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.eclipse.ui.IWorkbench;
+
+
+/**
+ * This class represents a preference page that
+ * is contributed to the Preferences dialog. By 
+ * subclassing <samp>FieldEditorPreferencePage</samp>, we
+ * can use the field support built into JFace that allows
+ * us to create a page that is small and knows how to 
+ * save, restore and apply itself.
+ * <p>
+ * This page is used to modify preferences only. They
+ * are stored in the preference store that belongs to
+ * the main plug-in class. That way, preferences can
+ * be accessed directly via the preference store.
+ */
+
+public class UserInfoPreferencePage
+	extends FieldEditorPreferencePage
+	implements IWorkbenchPreferencePage {
+
+	public UserInfoPreferencePage() {
+		super(GRID);
+		setPreferenceStore(NavigatorPlugin.getDefault().getPreferenceStore());
+		setDescription(Messages.getString("UserInfo.PREFERENCES_NAME"));
+	}
+	
+	/**
+	 * Creates the field editors. Field editors are abstractions of
+	 * the common GUI blocks needed to manipulate various types
+	 * of preferences. Each field editor knows how to save and
+	 * restore itself.
+	 */
+	@Override
+    public void createFieldEditors() {
+        addField(
+                new BooleanFieldEditor(
+                    UserInfoPreferenceConstants.AUTO_SAVE,
+                    Messages.getString("UserInfo.SAVE_ANNOTATION"), 
+                    getFieldEditorParent()));
+
+        /*
+        addField(
+                new BooleanFieldEditor(
+                    UserInfoPreferenceConstants.AUTO_REFRESH,
+                    Messages.getString("UserInfo.REFRESH_TREEVIEW"), 
+
+                    getFieldEditorParent()));
+        */
+        
+        //addField(
+        //        new BooleanFieldEditor(
+        //            UserInfoPreferenceConstants.NONVERBAL_OVERVIEW,
+        //            "&Nonverbal Overview",
+        //            getFieldEditorParent()));
+                    
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
+	 */
+	public void init(IWorkbench workbench) {
+	}
+	
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/ui/ModeContribution.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/ui/ModeContribution.java
new file mode 100644
index 0000000..61a52de
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/ui/ModeContribution.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.ui;
+
+import org.eclipse.jface.action.ControlContribution;
+import org.eclipse.jface.action.StatusLineLayoutData;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+
+
+
+public class ModeContribution extends ControlContribution {
+
+    public ModeContribution(String id) {
+        super(id);
+    }
+
+    public static final String MODE_CONTRIBUTION_ID = "navigator.mode";
+
+    private Composite c;
+
+    private Label nvm3;
+    
+    private String nvm3Text;
+
+    private Label mode;
+    
+    private String modeText;
+
+    private StatusLineLayoutData data;
+
+    @Override
+    protected Control createControl(Composite parent) {
+        data = new StatusLineLayoutData();
+        data.heightHint = 18;
+        c = new Composite(parent, SWT.NULL);
+        c.setLayoutData(data);
+        RowLayout rl = new RowLayout();
+        rl.fill = true;
+        rl.justify = true;
+        rl.wrap = false;
+        c.setLayout(rl);
+
+        nvm3 = new Label(c, SWT.NONE);
+        if(nvm3Text != null)
+            nvm3.setText(nvm3Text);
+        mode = new Label(c, SWT.NONE);
+        if(modeText != null)
+            mode.setText(modeText);
+        
+        return c;
+    }
+
+    public void setMode(String name) {
+        modeText = "  [" + name + "]";
+    }
+
+    public void showNVM3Name(String name) {
+        nvm3Text = name;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/ui/URLOpenDialog.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/ui/URLOpenDialog.java
new file mode 100644
index 0000000..1a310e6
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/ui/URLOpenDialog.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Daisuke SATO - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.navigator.ui;
+
+import org.eclipse.actf.ai.navigator.Messages;
+import org.eclipse.actf.util.ui.IDialogConstants;
+import org.eclipse.swt.SWT;
+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.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+
+
+
+public class URLOpenDialog {
+
+	private Shell _shell;
+
+	private Text _urlText;
+    
+    private String _url = "";
+
+	private int _returnCode = 0;
+
+	// Checked:030806
+	public URLOpenDialog(Shell shell) {
+		this._shell = new Shell(shell, SWT.DIALOG_TRIM | SWT.PRIMARY_MODAL);
+		this._shell.setLayout(new GridLayout());
+	}
+
+	// Checked:030806
+	private void createButtonControls() {
+		Composite composite = new Composite(this._shell, SWT.NULL);
+
+		GridData gridData = new GridData(GridData.HORIZONTAL_ALIGN_END | GridData.VERTICAL_ALIGN_END);
+		gridData.heightHint = 50;
+		composite.setLayoutData(gridData);
+
+		GridLayout layout = new GridLayout();
+		layout.numColumns = 3;
+		layout.horizontalSpacing = 20;
+		layout.marginWidth = 20;
+		layout.marginHeight = 10;
+		composite.setLayout(layout);
+
+		Button okButton = new Button(composite, SWT.PUSH);
+		okButton.setText(IDialogConstants.OK); //$NON-NLS-1$
+		okButton.addSelectionListener(new SelectionAdapter() {
+			@Override
+            public void widgetSelected(SelectionEvent e) {
+				_returnCode = 1;
+                _url = _urlText.getText();
+//				BrowserEventListenerManager.getInstance().fireSetAddressTextString(_url);
+				_shell.close();
+			}
+		});
+
+		Button cancelButton = new Button(composite, SWT.PUSH);
+		cancelButton.setText(IDialogConstants.CANCEL); //$NON-NLS-1$
+		cancelButton.addSelectionListener(new SelectionAdapter() {
+			@Override
+            public void widgetSelected(SelectionEvent e) {
+				_returnCode = 0;
+				_shell.close();
+			}
+		});
+
+        
+//		Button openButton = new Button(composite, SWT.PUSH);
+//		openButton.setText(DialogConst.BROWSE); //$NON-NLS-1$
+//		openButton.addSelectionListener(new SelectionAdapter() {
+//			public void widgetSelected(SelectionEvent e) {
+//				FileDialog openDialog = new FileDialog(_shell, SWT.OPEN);
+//				String openFile = openDialog.open();
+//
+//				if (openFile != null && !openFile.equals("")) { //$NON-NLS-1$
+//					_urlText.setText(openFile);
+//				}
+//			}
+//		});
+
+		this._shell.setDefaultButton(okButton);
+	}
+
+	// Checked:030806
+	private void createSettingControls() {
+		GridLayout gridLayout1;
+
+		Composite composite = new Composite(_shell, SWT.NULL);
+		composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+		gridLayout1 = new GridLayout();
+		gridLayout1.numColumns = 2;
+		composite.setLayout(gridLayout1);
+
+		// information
+		Label infoLabel = new Label(composite, SWT.NONE);
+		infoLabel.setText(IDialogConstants.OPENFILE_INFO);
+		GridData gridData = new GridData();
+		gridData.horizontalSpan = 2;
+		infoLabel.setLayoutData(gridData);
+
+		// URL label
+		Label label1 = new Label(composite, SWT.NONE);
+		label1.setText("URL: ");
+
+		// Create the TextBox
+		_urlText = new Text(composite, SWT.SINGLE | SWT.BORDER);
+		_urlText.setText("");
+
+		gridData = new GridData();
+		gridData.widthHint = 300;
+		_urlText.setLayoutData(gridData);
+	}
+
+	// Checked:030806
+	public int open() {
+		this._shell.setText(Messages.getString("DialogOpenURL.Open_URL_2"));
+
+		createSettingControls();
+
+		createButtonControls();
+		this._shell.setSize(375, 150);
+		this._shell.open();
+		this._shell.setLocation(100, 100);
+
+		Display display = _shell.getDisplay();
+		while (!_shell.isDisposed() || !display.readAndDispatch()) {
+			if (!display.readAndDispatch()) {
+				display.sleep();
+			}
+		}
+
+		return this._returnCode;
+	}
+    
+    public String getUrl(){
+        return this._url;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/IMetaDataModifier.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/IMetaDataModifier.java
new file mode 100644
index 0000000..db4eb90
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/IMetaDataModifier.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Masatomo KOBAYASHI - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.userinfo;
+
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.navigator.userinfo.IUserInfoGenerator.Result;
+import org.eclipse.actf.ai.xmlstore.XMLStoreException;
+
+
+
+
+public interface IMetaDataModifier {
+
+    String toString(Result result);
+
+    Result commit(boolean save) throws XMLStoreException;
+
+    String getSite();
+
+    void setSite(String targetSite);
+
+    ITreeItem getItem();
+
+    void setItem(ITreeItem item);
+
+    String getText();
+
+    void setText(String text);
+
+    IUserInfoGenerator getGenerator();
+
+    void setGenerator(IUserInfoGenerator generator);
+
+    boolean remove() throws XMLStoreException;
+
+    void setPageTitle(String title);
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/IUserInfoConstants.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/IUserInfoConstants.java
new file mode 100644
index 0000000..32ba92a
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/IUserInfoConstants.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Masatomo KOBAYASHI - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.userinfo;
+
+
+
+public interface IUserInfoConstants {
+    public static final String DEFAULT_NAMESPACE = "http://www.ibm.com/xmlns/prod/aiBrowser/fennec";
+    public static final String LOC_NAMESPACE = "http://www.ibm.com/xmlns/prod/aiBrowser/fennec/xml-query";
+    public static final String FLQ_NAMESPACE = "http://www.ibm.com/xmlns/prod/aiBrowser/fennec/flash-query";
+    public static final String MSQ_NAMESPACE = "http://www.ibm.com/xmlns/prod/aiBrowser/fennec/msaa-query";
+}
+
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/IUserInfoGenerator.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/IUserInfoGenerator.java
new file mode 100644
index 0000000..50abf46
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/IUserInfoGenerator.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Masatomo KOBAYASHI - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.userinfo;
+
+import org.w3c.dom.Node;
+
+
+public interface IUserInfoGenerator {
+    public static enum Result {
+        NOTHING,
+        CREATED,
+        REMOVED,
+        CHANGED,
+        ERROR,
+    }
+
+    String toString(Result result);
+//    Status addUserInfo(Node container, TargetNodeQuery paths, String altText);
+    Result addUserInfo(Node node, String altText);
+}
+
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/AltInputDialog.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/AltInputDialog.java
new file mode 100644
index 0000000..22d9661
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/AltInputDialog.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Masatomo KOBAYASHI - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.userinfo.impl;
+
+import org.eclipse.actf.ai.navigator.Messages;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+
+
+
+public class AltInputDialog extends Dialog {
+    
+    private String formInputString = "";
+
+    @Override
+    protected void configureShell(Shell newShell) {
+        super.configureShell(newShell);
+
+        newShell.setText(Messages.getString("AltInputDialog.Text"));
+    }
+
+    @Override
+    protected Control createDialogArea(Composite parent) {
+        Composite container = (Composite) super.createDialogArea(parent);
+        GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+        gd.widthHint = 400;
+        gd.horizontalSpan = ((GridLayout) parent.getLayout()).numColumns;
+        
+        final Text formInputField = new Text(container, SWT.BORDER);
+        formInputField.setLayoutData(gd);
+        formInputField.setText(formInputString);
+        formInputField.selectAll();
+
+        formInputField.addModifyListener(new ModifyListener() {
+            public void modifyText(ModifyEvent e) {
+                formInputString = formInputField.getText();
+            }
+        });
+        return container;
+    }
+
+    public String getResult() {
+        return formInputString;
+    }
+
+    public AltInputDialog(Shell parent, String init) {
+        super(parent);
+        this.formInputString = init;
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/AltTextEditor.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/AltTextEditor.java
new file mode 100644
index 0000000..fc55875
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/AltTextEditor.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Masatomo KOBAYASHI - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.userinfo.impl;
+
+import org.eclipse.actf.ai.navigator.userinfo.IUserInfoConstants;
+import org.eclipse.actf.ai.navigator.userinfo.IUserInfoGenerator;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+
+
+
+public class AltTextEditor implements IUserInfoGenerator, IUserInfoConstants {
+    
+//    @Override
+//    public String toString() {
+//        return "ALT text";
+//    }
+
+    public String toString(Result result) {
+        switch (result) {
+        case CREATED:
+            return "AltTextEditor.Result.CREATED";
+        case REMOVED:
+            return "AltTextEditor.Result.REMOVED";
+        case CHANGED:
+            return "AltTextEditor.Result.CHANGED";
+        case ERROR:
+            return "AltTextEditor.Result.ERROR";
+        }
+        return "";
+    }
+    
+    private Node getExistingUserInfo(Node node) {
+        for (Node c = node.getFirstChild(); c != null; c = c.getNextSibling()) {
+            String ns = c.getNamespaceURI();
+            if ((ns == null) || (!ns.equals(DEFAULT_NAMESPACE))) continue;
+            String n = c.getLocalName();
+            if (n.equals("altText"))
+                return c;
+        }
+        return null;
+    }
+    
+    public Result addUserInfo(Node node, String text) {
+        Document doc = node.getOwnerDocument();
+        Node info = getExistingUserInfo(node);
+        if (text.length() > 0) {
+            if (info == null) {
+                info = node.appendChild(doc.createElementNS(DEFAULT_NAMESPACE, "altText"));
+                info.setTextContent(text);
+                return Result.CREATED;
+            }
+            else {
+                info.setTextContent(text);
+                return Result.CHANGED;
+            }
+        }   
+        // TODO ? which is better: "" -> remove the alt text added by the user or set an empty alt text
+        else if (info != null) {
+            node.removeChild(info);
+            return Result.REMOVED;
+        }
+        return Result.NOTHING;
+    }
+    
+//    public Status addUserInfo(Node parent, TargetNodeQuery paths, String text) {
+//        Document doc = parent.getOwnerDocument();
+//        Node node = paths.getNode(parent);
+//        Node info = getExistingUserInfo(node);
+//
+//        if (text.length() > 0) {
+//            if (info == null) {
+//                info = node.appendChild(doc.createElementNS(DEFAULT_NAMESPACE, "altText"));
+//                info.setTextContent(text);
+//                return Status.CREATED;
+//            }
+//            else {
+//                info.setTextContent(text);
+//                return Status.CHANGED;
+//            }
+//        }   
+//        
+//        // TODO ? which is better: "" -> remove the alt text added by the user or set an empty alt text
+//        
+//        if (info != null) {
+//            node.removeChild(info);
+//            if (!node.hasChildNodes())
+//                parent.removeChild(node);
+//            return Status.REMOVED;
+//        }
+//
+//        return Status.NOTHING;
+//    }
+
+//    @Override
+//    protected String buildXML(String attr, String base, String text) {
+//        StringBuffer ret = new StringBuffer();
+//        ret.append("<attach ");
+//        ret.append(attr);
+//        ret.append("=\"");
+//        ret.append(base);
+//        ret.append("\"");
+//        ret.append("><altText>");
+//        ret.append(text);
+//        ret.append("</altText></attach>");
+//        return ret.toString();
+//    }
+    
+//    @Override
+//    public String buildXML(Map<String, String> attr, String text) {
+//        StringBuffer ret = new StringBuffer();
+//        ret.append("<!--attach loc:path=\".\"--><group");
+//        for (Map.Entry<String, String> e : attr.entrySet()) {
+//            ret.append("\n  ");
+//            ret.append(e.getKey());
+//            ret.append("=\"");
+//            ret.append(e.getValue());
+//            ret.append("\"");
+//        }
+//        ret.append(">\n  <altText>");
+//        ret.append(text);
+//        ret.append("</altText>\n</group><!--/attach-->");
+//        return ret.toString();
+//    }
+    
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/AltTextGuesser.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/AltTextGuesser.java
new file mode 100644
index 0000000..a8c5a20
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/AltTextGuesser.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Masatomo KOBAYASHI - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.userinfo.impl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.model.IWebBrowserACTF;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+
+
+
+public class AltTextGuesser {
+    private final IWebBrowserACTF browser;
+    private final ITreeItem item;
+    
+    public AltTextGuesser(IWebBrowserACTF browser, ITreeItem item) {
+        this.browser = browser;
+        this.item = item;
+    }
+    
+    public String getDefaultText() {
+        return item.getUIString();
+    }
+    
+    public String guessByOCR() {
+        throw new RuntimeException("Not implemented.");
+    }
+    
+    public String guessByContext() {
+        return getContext(browser, getContext(item));
+    }
+    
+    private String getContext(ITreeItem item) {
+        Object o = item.getBaseNode();
+        if (!(o instanceof Node))
+            return null;
+        for (Node n = (Node) o; n != null; n = n.getParentNode()) {
+//            System.err.println(n.getClass());
+            if (n instanceof Document)
+                break;
+            String name = n.getLocalName();
+            // I'd like to use getLinkURI!!
+            if ("A".equals(name)) {
+//                System.out.println(n.getClass());
+                NamedNodeMap v = n.getAttributes();
+                if (v == null)
+                    continue;
+                
+                Node a = v.getNamedItem("href");
+                if (a == null)
+                    continue;
+                return a.getNodeValue();
+            }
+        }
+        return null;
+    }
+    
+    private String getContext(IWebBrowserACTF browser, String path) {
+        try {
+            URL url =  new URL(new URL(browser.getURL()), path);
+            String s = null;
+            String title = "", h1 = "";
+            Pattern pattern = Pattern.compile("<(?:(title)|(h1))[^>]*>([^<]*)</(?:title|h1)>" , Pattern.CASE_INSENSITIVE);
+            BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
+            while ((s = br.readLine()) != null) { // TODO ad-hoc
+                Matcher matcher = pattern.matcher(s);
+                if (!matcher.matches())
+                    continue;
+                if (matcher.group(1) != null)
+                    title = matcher.group(3);
+                if (matcher.group(2) != null)
+                    h1 = matcher.group(3);
+                if (title.length() > 0 && h1.length() > 0)
+                    break;
+            }
+            br.close();
+            System.out.println("URL\t" + url);
+            System.out.println("title\t" + title);
+            System.out.println("h1\t" + h1);
+            return title.length() > h1.length() ? title : h1;
+        } catch (MalformedURLException e) {
+//          e.printStackTrace();
+            return null;
+        } catch (IOException e) {
+//          e.printStackTrace();
+            return null;
+        }
+    }
+    
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/BrowserObserver.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/BrowserObserver.java
new file mode 100644
index 0000000..a2ac38d
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/BrowserObserver.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Masatomo KOBAYASHI - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.userinfo.impl;
+
+import org.eclipse.actf.model.IWebBrowserACTF;
+
+
+
+public class BrowserObserver {
+    private IWebBrowserACTF webBrowser;
+    
+    public BrowserObserver(IWebBrowserACTF webBrowser) {
+        this.webBrowser = webBrowser;
+    }
+    
+    public String getTargetFilter() { // TODO
+        String s = webBrowser.getURL();
+        int i = s.indexOf('?');
+        if (i >= 0)
+            s = s.substring(0, i).concat("*");
+        return s;
+    }
+
+    public IWebBrowserACTF getWebBrowser() {
+        return webBrowser;
+    }
+
+    public void setWebBrowser(IWebBrowserACTF webBrowser) {
+        this.webBrowser = webBrowser;
+    }
+    
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/HeadingCanceller.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/HeadingCanceller.java
new file mode 100644
index 0000000..610ccad
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/HeadingCanceller.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Masatomo KOBAYASHI - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.userinfo.impl;
+
+import org.eclipse.actf.ai.navigator.userinfo.IUserInfoConstants;
+import org.eclipse.actf.ai.navigator.userinfo.IUserInfoGenerator;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+
+
+
+public class HeadingCanceller implements IUserInfoGenerator, IUserInfoConstants {
+
+//    @Override
+//    public String toString() {
+//        return "landmark";
+//    }
+    
+    public String toString(Result result) {
+        switch (result) {
+        case CREATED:
+            return "HeadingCanceller.Result.CREATED";
+        case REMOVED:
+            return "HeadingCanceller.Result.REMOVED";
+        case ERROR:
+            return "HeadingCanceller.Result.ERROR";
+        case NOTHING:
+            return "HeadingCanceller.Result.NOTHING";
+        }
+        return "";
+    }
+
+    private Node getExistingUserInfo(Node node) {
+        for (Node c = node.getFirstChild(); c != null; c = c.getNextSibling()) {
+            String ns = c.getNamespaceURI();
+            if ((ns == null) || (!ns.equals(DEFAULT_NAMESPACE))) continue;
+            String n = c.getLocalName();
+            if (n.equals("h1") || n.equals("h-"))
+                return c;
+        }
+        return null;
+    }
+    
+    public Result addUserInfo(Node node, String text) {
+        Document doc = node.getOwnerDocument();
+        Node info = getExistingUserInfo(node);
+        if (info != null) {
+            if (info.getNodeName().equals("h-"))
+                return Result.NOTHING;
+            node.removeChild(info);
+//          if (!node.hasChildNodes())
+//              parent.removeChild(node);
+            return Result.REMOVED;
+        }
+        node.appendChild(doc.createElementNS(DEFAULT_NAMESPACE, "h-"));
+//      node.appendChild(doc.createElementNS(DEFAULT_NAMESPACE, "altText")).appendChild(doc.createTextNode(text));
+        return Result.CREATED;
+    }
+    
+//    public Status addUserInfo(Node parent, TargetNodeQuery paths, String text) {
+//        Document doc = parent.getOwnerDocument();
+//        Node node = paths.getNode(parent);
+//        Node info = getExistingUserInfo(node);
+//        if (info != null) {
+//            node.removeChild(info);
+//            if (!node.hasChildNodes())
+//                parent.removeChild(node);
+//            return Status.REMOVED;
+//        }
+//        else {
+//            node.appendChild(doc.createElementNS(DEFAULT_NAMESPACE, "h-"));
+////          node.appendChild(doc.createElementNS(DEFAULT_NAMESPACE, "altText")).appendChild(doc.createTextNode(text));
+//            return Status.CREATED;
+//        }
+//    }
+    
+//    @Override
+//    protected String buildXML(String attr, String base, String text) {
+//        StringBuffer ret = new StringBuffer();
+//        ret.append("<attach ");
+//        ret.append(attr);
+//        ret.append("=\"");
+//        ret.append(base);
+//        ret.append("\"");
+//        ret.append("><h1/><altText>");
+//        ret.append(text);
+//        ret.append("</altText></attach>");
+//        return ret.toString();
+//    }
+    
+//    @Override
+//    public String buildXML(Map<String, String> attr, String text) {
+//        StringBuffer ret = new StringBuffer();
+//        ret.append("<!--attach loc:path=\".\"--><group");
+//        for (Map.Entry<String, String> e : attr.entrySet()) {
+//            ret.append("\n  ");
+//            ret.append(e.getKey());
+//            ret.append("=\"");
+//            ret.append(e.getValue());
+//            ret.append("\"");
+//        }
+//        ret.append(">\n  <h1/>\n  <altText>");
+//        ret.append(text);
+//        ret.append("</altText>\n</group><!--/attach-->");
+//        return ret.toString();
+//    }
+
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/LandmarkMaker.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/LandmarkMaker.java
new file mode 100644
index 0000000..1877fd4
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/LandmarkMaker.java
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Masatomo KOBAYASHI - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.userinfo.impl;
+
+import org.eclipse.actf.ai.navigator.userinfo.IUserInfoConstants;
+import org.eclipse.actf.ai.navigator.userinfo.IUserInfoGenerator;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+
+
+
+public class LandmarkMaker implements IUserInfoGenerator, IUserInfoConstants {
+
+//    @Override
+//    public String toString() {
+//        return "landmark";
+//    }
+    
+    public String toString(Result result) {
+        switch (result) {
+        case CREATED:
+            return "LandmarkMaker.Result.CREATED";
+        case REMOVED:
+            return "LandmarkMaker.Result.REMOVED";
+        case ERROR:
+            return "LandmarkMaker.Result.ERROR";
+        case NOTHING:
+            return "LandmarkMaker.Result.NOTHING";
+        }
+        return "";
+    }
+
+    private Node getExistingUserInfo(Node node) {
+        for (Node c = node.getFirstChild(); c != null; c = c.getNextSibling()) {
+            String ns = c.getNamespaceURI();
+            if ((ns == null) || (!ns.equals(DEFAULT_NAMESPACE))) continue;
+            String n = c.getLocalName();
+            if (n.equals("h-") || n.equals("h1"))
+                return c;
+        }
+        return null;
+    }
+    
+    public Result addUserInfo(Node node, String text) {
+        Document doc = node.getOwnerDocument();
+        Node info = getExistingUserInfo(node);
+        if (info != null) {
+            if (info.getNodeName().equals("h1"))
+                return Result.NOTHING;
+            node.removeChild(info);
+//          if (!node.hasChildNodes())
+//              parent.removeChild(node);
+            return Result.REMOVED;
+        }
+        node.appendChild(doc.createElementNS(DEFAULT_NAMESPACE, "h1"));
+//      node.appendChild(doc.createElementNS(DEFAULT_NAMESPACE, "altText")).appendChild(doc.createTextNode(text));
+        return Result.CREATED;
+    }
+    
+//    public Status addUserInfo(Node parent, TargetNodeQuery paths, String text) {
+//        Document doc = parent.getOwnerDocument();
+//        Node node = paths.getNode(parent);
+//        Node info = getExistingUserInfo(node);
+//        if (info != null) {
+//            node.removeChild(info);
+//            if (!node.hasChildNodes())
+//                parent.removeChild(node);
+//            return Status.REMOVED;
+//        }
+//        else {
+//            node.appendChild(doc.createElementNS(DEFAULT_NAMESPACE, "h1"));
+////          node.appendChild(doc.createElementNS(DEFAULT_NAMESPACE, "altText")).appendChild(doc.createTextNode(text));
+//            return Status.CREATED;
+//        }
+//    }
+    
+//    @Override
+//    protected String buildXML(String attr, String base, String text) {
+//        StringBuffer ret = new StringBuffer();
+//        ret.append("<attach ");
+//        ret.append(attr);
+//        ret.append("=\"");
+//        ret.append(base);
+//        ret.append("\"");
+//        ret.append("><h1/><altText>");
+//        ret.append(text);
+//        ret.append("</altText></attach>");
+//        return ret.toString();
+//    }
+    
+//    @Override
+//    public String buildXML(Map<String, String> attr, String text) {
+//        StringBuffer ret = new StringBuffer();
+//        ret.append("<!--attach loc:path=\".\"--><group");
+//        for (Map.Entry<String, String> e : attr.entrySet()) {
+//            ret.append("\n  ");
+//            ret.append(e.getKey());
+//            ret.append("=\"");
+//            ret.append(e.getValue());
+//            ret.append("\"");
+//        }
+//        ret.append(">\n  <h1/>\n  <altText>");
+//        ret.append(text);
+//        ret.append("</altText>\n</group><!--/attach-->");
+//        return ret.toString();
+//    }
+
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/MetaDataModifier.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/MetaDataModifier.java
new file mode 100644
index 0000000..9baafd7
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/userinfo/impl/MetaDataModifier.java
@@ -0,0 +1,199 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Masatomo KOBAYASHI - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.userinfo.impl;
+
+import java.util.Iterator;
+
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.navigator.userinfo.IMetaDataModifier;
+import org.eclipse.actf.ai.navigator.userinfo.IUserInfoConstants;
+import org.eclipse.actf.ai.navigator.userinfo.IUserInfoGenerator;
+import org.eclipse.actf.ai.navigator.userinfo.IUserInfoGenerator.Result;
+import org.eclipse.actf.ai.xmlstore.IXMLEditableInfo;
+import org.eclipse.actf.ai.xmlstore.IXMLInfo;
+import org.eclipse.actf.ai.xmlstore.IXMLSelector;
+import org.eclipse.actf.ai.xmlstore.IXMLStore;
+import org.eclipse.actf.ai.xmlstore.IXMLStoreService;
+import org.eclipse.actf.ai.xmlstore.XMLStoreException;
+import org.eclipse.actf.ai.xmlstore.XMLStorePlugin;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+
+
+
+public class MetaDataModifier implements IUserInfoConstants, IMetaDataModifier {
+    private IUserInfoGenerator generator;
+    private ITreeItem item;
+    private String text;
+    private String site;
+    private String pageTitle;
+    
+//    @Override
+//    public String toString() {
+//        StringBuffer ret = new StringBuffer();
+//        String s = item.getUIString();
+//        ret.append(generator);
+//        ret.append(" : ");
+//        ret.append(s != null && s.length() > 0 ? s : "X");
+//        ret.append(" is ");
+//        ret.append(text);
+//        return ret.toString();
+//    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.actf.ai.navigator.userinfo.IMetaDataModifier#toString(org.eclipse.actf.ai.navigator.userinfo.IUserInfoConstants.Result)
+     */
+    public String toString(Result result) {
+        return generator.toString(result);
+    }
+    
+    /* (non-Javadoc)
+     * @see org.eclipse.actf.ai.navigator.userinfo.IMetaDataModifier#commit(boolean)
+     */
+    public Result commit(boolean save) throws XMLStoreException {
+        IXMLEditableInfo info = getUserInfo();
+        info.setPageTitle(pageTitle);
+        Result result = Result.NOTHING;
+        if (generator != null) {
+            if (item == null) return Result.ERROR;
+            Document infoDocument = info.getRootNode().getOwnerDocument();
+            Node parent = infoDocument.getElementsByTagNameNS(DEFAULT_NAMESPACE, "attach").item(0);
+//            TargetNodeQuery paths = getQueryPaths(parent.getOwnerDocument());
+//            result = generator.addUserInfo(parent, paths, text);
+//            Node node = getInfoNode(parent);
+            Node node = item.serializeQuery(parent);
+            if (node == null) return Result.ERROR;
+            result = generator.addUserInfo(node, text);
+            if (!node.hasChildNodes())
+                parent.removeChild(node);
+        }
+        if (save)
+            info.save();
+        return result;
+    }
+    
+    private IXMLEditableInfo getUserInfo() throws XMLStoreException {
+        IXMLStoreService service = XMLStorePlugin.getDefault().getXMLStoreService();
+        IXMLEditableInfo info = getStoredUserInfo(service);
+        if (info != null) {
+            return info;
+        } else {
+            return createNewUserInfo(service);
+        }
+    }
+    
+    private IXMLEditableInfo getStoredUserInfo(IXMLStoreService service) {
+        IXMLStore store0 = service.getRootStore();
+        IXMLSelector sel1 = service.getSelectorWithDocElem("fennec", DEFAULT_NAMESPACE);
+        IXMLStore store1 = store0.specify(sel1);
+        if (store1 == null)
+            return null;
+        IXMLSelector sel2 = service.getSelectorWithIRI(site);
+        IXMLStore store2 = store1.specify(sel2);
+        if (store2 == null)
+            return null;
+        for (Iterator<IXMLInfo> i = store2.getInfoIterator(); i.hasNext(); ) {
+            IXMLInfo info = i.next();
+            if (info.isUserEntry() && (info instanceof IXMLEditableInfo)) {
+                //  System.out.println("open stored user info.");
+                return (IXMLEditableInfo) info;
+            }
+        }
+        return null;
+    }
+    
+    private IXMLEditableInfo createNewUserInfo(IXMLStoreService service) throws XMLStoreException {
+        IXMLEditableInfo info = service.newUserXML(DEFAULT_NAMESPACE, "fennec", site);
+        info.setPageTitle(pageTitle);
+        Node root = info.getRootNode();
+        Document doc = root.getOwnerDocument();
+        Element node = doc.createElementNS(DEFAULT_NAMESPACE, "attach");
+        node.setAttributeNS(LOC_NAMESPACE, "loc:path", ".");
+        root.appendChild(node);
+        //System.out.println("create new user info.");
+        return info;
+    }
+    
+    public boolean remove() throws XMLStoreException {
+        IXMLStoreService service = XMLStorePlugin.getDefault().getXMLStoreService();
+        IXMLEditableInfo info = getStoredUserInfo(service);
+
+        if (info == null) {
+            return false;
+        }
+        info.remove();
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.actf.ai.navigator.userinfo.IMetaDataModifier#getSite()
+     */
+    public String getSite() {
+        return site;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.actf.ai.navigator.userinfo.IMetaDataModifier#setSite(java.lang.String)
+     */
+    public void setSite(String targetSite) {
+        this.site = targetSite;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.actf.ai.navigator.userinfo.IMetaDataModifier#getItem()
+     */
+    public ITreeItem getItem() {
+        return item;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.actf.ai.navigator.userinfo.IMetaDataModifier#setItem(org.eclipse.actf.ai.fennec.treemanager.ITreeItem)
+     */
+    public void setItem(ITreeItem item) {
+        this.item = item;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.actf.ai.navigator.userinfo.IMetaDataModifier#getText()
+     */
+    public String getText() {
+        return text;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.actf.ai.navigator.userinfo.IMetaDataModifier#setText(java.lang.String)
+     */
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.actf.ai.navigator.userinfo.IMetaDataModifier#getGenerator()
+     */
+    public IUserInfoGenerator getGenerator() {
+        return generator;
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.actf.ai.navigator.userinfo.IMetaDataModifier#setGenerator(org.eclipse.actf.ai.navigator.userinfo.IUserInfoGenerator)
+     */
+    public void setGenerator(IUserInfoGenerator generator) {
+        this.generator = generator;
+    }
+    
+    public void setPageTitle(String pageTitle) {
+        this.pageTitle = pageTitle;
+    }
+}
+
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/util/ContentShortener.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/util/ContentShortener.java
new file mode 100644
index 0000000..28dbafe
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/util/ContentShortener.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Masatomo KOBAYASHI - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.actf.ai.navigator.util;
+
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+
+
+
+public class ContentShortener {
+    private int limit;
+
+    public int getLimit() {
+        return limit;
+    }
+
+    public void setLimit(int max) {
+        this.limit = max;
+    }
+
+    private final MessageFormatter messageFormatter;
+    
+    public ContentShortener(int max,
+                            MessageFormatter messageFormatter) {
+        this.limit = max;
+        this.messageFormatter = messageFormatter;
+    }
+    
+    public String getSummary(ITreeItem item, boolean verbal) {
+        String s = item.getUIString();
+        if (s.length() <= limit) {
+            return s;
+        } else {
+            s = s.substring(0, limit);
+            if (verbal) {
+                return messageFormatter.mes("ContentShortener.VerbalForm", s);
+            } else {
+                return messageFormatter.mes("ContentShortener.NonVerbalForm", s);
+            }
+        }
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/util/MessageFormatter.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/util/MessageFormatter.java
new file mode 100644
index 0000000..dad8664
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/util/MessageFormatter.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.navigator.util;
+
+import java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+public class MessageFormatter {
+    private ResourceBundle resourceBundle = null;
+
+    private void setResourceBundle(String pkgName) {
+        try {
+            resourceBundle = ResourceBundle.getBundle(pkgName);
+        } catch (MissingResourceException e) {
+            resourceBundle = null;
+        }
+    }
+
+    public MessageFormatter(String pkgName) {
+        setResourceBundle(pkgName);
+    }
+
+    private String getFormat(String propName) {
+        if (resourceBundle == null) {
+            return propName;
+        }
+        String format;
+        try {
+            format = resourceBundle.getString(propName);
+        } catch (MissingResourceException e) {
+            return propName;
+        }
+        if (format != null)
+            return format;
+        return propName;
+    }
+
+    public String mes(String propName, Object[] args) {
+        String format = getFormat(propName);
+        String message = MessageFormat.format(format, args);
+        return message;
+    }
+
+    public String mes(String propName, Object obj1) {
+        return mes(propName, new Object[] { obj1 });
+    }
+
+    public String mes(String propName, Object obj1, Object obj2) {
+        return mes(propName, new Object[] { obj1, obj2 });
+    }
+
+    public String mes(String propName, Object obj1, Object obj2, Object obj3) {
+        return mes(propName, new Object[] { obj1, obj2, obj3 });
+    }
+
+    public String mes(String propName, Object obj1, Object obj2, Object obj3, Object obj4) {
+        return mes(propName, new Object[] { obj1, obj2, obj3, obj4 });
+    }
+
+    public String mes(String propName) {
+        return getFormat(propName);
+    }
+    
+    public String concat(String... args){
+        if (args.length == 0)
+            return "";
+        if (args.length == 1)
+            return args[0];
+        
+        StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < args.length-1; i++) {
+            buf.append(args[i]);
+            buf.append(" ");
+        }
+        buf.append(args[args.length-1]);
+        return buf.toString();
+    }
+}
diff --git a/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/views/NavigatorTreeView.java b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/views/NavigatorTreeView.java
new file mode 100644
index 0000000..209f8c0
--- /dev/null
+++ b/plugins/org.eclipse.actf.ai.navigator/src/org/eclipse/actf/ai/navigator/views/NavigatorTreeView.java
@@ -0,0 +1,314 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and Others
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Hisashi MIYASHITA - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.actf.ai.navigator.views;
+
+import org.eclipse.actf.ai.fennec.treemanager.ITreeItem;
+import org.eclipse.actf.ai.navigator.IManipulator;
+import org.eclipse.actf.ai.navigator.Messages;
+import org.eclipse.actf.ai.navigator.NavigatorPlugin;
+import org.eclipse.actf.ai.navigator.ui.ModeContribution;
+import org.eclipse.actf.util.vocab.Vocabulary;
+import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.ViewPart;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+
+public class NavigatorTreeView extends ViewPart {
+    public static final String ID = "org.eclipse.actf.ai.navigator.views.NavigatorTreeView"; //$NON-NLS-1$
+
+    private TreeViewer treeViewer;
+
+    private TableViewer tableViewer;
+
+    private boolean disposed;
+
+    static class ITreeItemLabelProvider extends LabelProvider {
+        @Override
+        public Image getImage(Object element) {
+            // TODO
+            return null;
+        }
+
+        @Override
+        public String getText(Object element) {
+            ITreeItem item = (ITreeItem) element;
+            String r = item.getUIString();
+            if (r.length() > 0)
+                return r;
+            r = item.getNodeString();
+            if (r == null)
+                return ""; //$NON-NLS-1$
+            return r;
+        }
+    }
+
+    static class ITreeItemContentProvider extends ArrayContentProvider implements ITreeContentProvider {
+
+        public Object[] getChildren(Object parentElement) {
+            ITreeItem item = (ITreeItem) parentElement;
+            return item.getChildItems();
+        }
+
+        public Object getParent(Object element) {
+            ITreeItem item = (ITreeItem) element;
+            return item.getParent();
+        }
+
+        public boolean hasChildren(Object element) {
+            ITreeItem item = (ITreeItem) element;
+            ITreeItem[] childItems = item.getChildItems();
+            if ((childItems != null) && (childItems.length > 0))
+                return true;
+            return false;
+        }
+
+        @Override
+        public Object[] getElements(Object input) {
+            return getChildren(input);
+        }
+    }
+
+    static class TreeItemDetailsContentProvider implements IStructuredContentProvider {
+        public void inputChanged(Viewer v, Object oldInput, Object newInput) {
+        }
+
+        public void dispose() {
+        }
+
+        private String calcPath(ITreeItem item) {
+            StringBuffer ret = new StringBuffer();
+            Object o = item.getBaseNode();
+            if (!(o instanceof Node))
+                return "#none"; //$NON-NLS-1$
+            for (Node n = (Node) o; n != null; n = n.getParentNode()) {
+                if (n instanceof Document)
+                    break;
+                ret.insert(0, n.getNodeName());
+                ret.insert(0, "/"); //$NON-NLS-1$
+            }
+            return ret.toString();
+        }
+
+        private String calcID(ITreeItem item) {
+            Object o = item.getBaseNode();
+            if (!(o instanceof Element))
+                return ""; //$NON-NLS-1$
+            Element e = (Element) o;
+            return e.getAttribute("id"); //$NON-NLS-1$
+        }
+
+        private String formatStringInHex(String str) {
+            StringBuffer ret = new StringBuffer();
+            for (int i = 0; i < str.length(); i++) {
+                char c = str.charAt(i);
+                ret.append(Integer.toHexString(c));
+                ret.append(" "); //$NON-NLS-1$
+            }
+            return ret.toString();
+        }
+
+        public Object[] getElements(Object obj) {
+            ITreeItem item = (ITreeItem) obj;
+            String[] uiStrings = { "UIString", item.getUIString() }; //$NON-NLS-1$
+            String[] uiStringsInHex = { "UIString (HEX)", formatStringInHex(item.getUIString()) }; //$NON-NLS-1$
+            String[] nodeStrings = { "Node", item.getNodeString() }; //$NON-NLS-1$
+            String[] path = { "Path", calcPath(item) }; //$NON-NLS-1$
+            String[] id = { "ID", calcID(item) }; //$NON-NLS-1$
+            String[] hasContent = { "hasContent", "" + Vocabulary.hasContent().eval(item)}; //$NON-NLS-1$ //$NON-NLS-2$
+            String[] heading = { "Heading", "" + item.getHeadingLevel() }; //$NON-NLS-1$ //$NON-NLS-2$
+            String[] nodeObject = { "NodeObject", "" + item.getBaseNode() }; //$NON-NLS-1$ //$NON-NLS-2$
+            String[] isInputable = { "isInputable", "" + item.isInputable() }; //$NON-NLS-1$ //$NON-NLS-2$
+            String[] isConnectable = { "isConnectable", "" + Vocabulary.isConnectable().eval(item) }; //$NON-NLS-1$ //$NON-NLS-2$
+            return new Object[] { uiStrings, uiStringsInHex, nodeStrings, path, id, hasContent, heading, nodeObject,
+                                  isInputable, isConnectable };
+        }
+    }
+
+    static class TreeItemDetailsLabelProvider extends LabelProvider implements ITableLabelProvider {
+        public String getColumnText(Object obj, int index) {
+            String[] prop = (String[]) obj;
+            if (index >= prop.length)
+                return null;
+            return prop[index];
+        }
+
+        public Image getColumnImage(Object obj, int index) {
+            return getImage(obj);
+        }
+
+        @Override
+        public Image getImage(Object obj) {
+            // TODO
+            return null;
+        }
+    }
+
+    class TreeItemViewerDoubleClickListener implements IDoubleClickListener {
+        public void doubleClick(DoubleClickEvent event) {
+            IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+            Object obj = sel.getFirstElement();
+            tableViewer.setInput(obj);
+        }
+    }
+
+    @Override
+    public void dispose() {
+        super.dispose();
+        disposed = true;
+    }
+
+    void initView(Composite parent) {
+        Layout layout = new FillLayout(SWT.VERTICAL);
+        parent.setLayout(layout);
+        
+        SashForm sash = new SashForm(parent, SWT.VERTICAL);
+        
+        treeViewer = new TreeViewer(sash);
+        treeViewer.setLabelProvider(new ITreeItemLabelProvider());
+        treeViewer.setContentProvider(new ITreeItemContentProvider());
+        treeViewer.addDoubleClickListener(new TreeItemViewerDoubleClickListener());
+        // treeViewer.setAutoExpandLevel(0);
+
+        tableViewer = new TableViewer(sash, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+        Table table = tableViewer.getTable();
+        table.setHeaderVisible(true);
+        table.setLinesVisible(true);
+        TableColumn tableColumnProperty = new TableColumn(table, SWT.LEFT);
+        tableColumnProperty.setText(Messages.getString("NavigatorTreeView.property")); //$NON-NLS-1$
+        tableColumnProperty.setWidth(70);
+        TableColumn tableColumnValue = new TableColumn(table, SWT.LEFT);
+        tableColumnValue.setText(Messages.getString("NavigatorTreeView.value")); //$NON-NLS-1$
+        tableColumnValue.setWidth(100);
+
+        tableViewer.setLabelProvider(new TreeItemDetailsLabelProvider());
+        tableViewer.setContentProvider(new TreeItemDetailsContentProvider());
+
+        sash.setWeights(new int[]{90, 10});
+    }
+
+    public NavigatorTreeView() {
+        super();
+    }
+
+    @Override
+    public void createPartControl(Composite parent) {
+        initView(parent);
+        NavigatorPlugin.getDefault().setNavigatorTreeView(this);
+    }
+
+    @Override
+    public void setFocus() {
+    }
+
+    private ITreeItem getRoot(ITreeItem item) {
+        for (ITreeItem parent = item.getParent(); parent != null; parent = item.getParent()) {
+            item = parent;
+        }
+        return item;
+    }
+
+    public void showItem(ITreeItem item) {
+        if (disposed) return;
+        if (treeViewer.getControl().isDisposed()) return;
+        treeViewer.setInput(getRoot(item));
+        treeViewer.reveal(item);
+        ISelection is = new StructuredSelection(item);
+        treeViewer.setSelection(is, true);
+    }
+
+    public void clearItem() {
+        if (disposed) return;
+        treeViewer.setInput(null);
+    }
+
+    public boolean isShown() {
+        IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+        IViewPart part = page.findView(ID);
+        return part != null;
+    }
+
+    public boolean toggleViewShowing() {
+        IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+        IViewPart part = page.findView(ID);
+
+        if (part != null) {
+            page.hideView(part);
+            return false;
+        } else {
+            try {
+                page.showView(ID);
+                return true;
+            } catch (PartInitException e) {
+                e.printStackTrace();
+            }
+        }
+        return false;
+    }
+
+    private ModeContribution getModeContribution(String id) {
+        IViewSite viewSite = getViewSite();
+        IActionBars actionBars = viewSite.getActionBars();
+        IStatusLineManager manager = actionBars.getStatusLineManager();
+        return (ModeContribution) manager.find(id);
+    }
+    private void update(){
+        IViewSite viewSite = getViewSite();
+        IActionBars actionBars = viewSite.getActionBars();
+        IStatusLineManager manager = actionBars.getStatusLineManager();
+        manager.update(true);
+    }
+
+
+    public void setMode(IManipulator.Mode mode) {
+        ModeContribution mc = getModeContribution(ModeContribution.MODE_CONTRIBUTION_ID);
+        if (mc != null) {