Index: src/org/mozilla/solid/wtp/editors/MultiPageEditorContributor.java
===================================================================
RCS file: /cvs/solid/src/org.mozilla.solid.wtp/src/org/mozilla/solid/wtp/editors/MultiPageEditorContributor.java,v
retrieving revision 1.1
diff -u -r1.1 MultiPageEditorContributor.java
--- src/org/mozilla/solid/wtp/editors/MultiPageEditorContributor.java	11 Jul 2005 12:22:59 -0000	1.1
+++ src/org/mozilla/solid/wtp/editors/MultiPageEditorContributor.java	17 Jul 2005 20:46:28 -0000
@@ -11,6 +11,8 @@
 
 package org.mozilla.solid.wtp.editors;
 
+import java.io.IOException;
+
 import org.eclipse.jface.action.*;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.ui.IActionBars;
@@ -23,6 +25,8 @@
 import org.eclipse.ui.part.MultiPageEditorActionBarContributor;
 import org.eclipse.ui.texteditor.ITextEditor;
 import org.eclipse.ui.texteditor.ITextEditorActionConstants;
+import org.mozilla.solid.wtp.sockets.MozillaClient;
+import org.mozilla.solid.wtp.sockets.SolidProtocolException;
 
 /**
  * Manages the installation/deinstallation of global actions for multi-page editors.
@@ -94,7 +98,20 @@
 	private void createActions() {
 		sampleAction = new Action() {
 			public void run() {
-				MessageDialog.openInformation(null, "Whack Plug-in", "Sample Action Executed");
+				//MessageDialog.openInformation(null, "Whack Plug-in", "Sample Action Executed");
+				try {
+					MessageDialog.openInformation(null, "Whack Plug-in", 
+							MozillaClient.test() ? "OK" : "Failure");
+				} catch(SolidProtocolException e) {
+					MessageDialog.openInformation(null, "Whack Plug-in", 
+							"MozillaClient.test() failed: " + e);
+				} catch(IOException e) {
+					MessageDialog.openInformation(null, "Whack Plug-in", 
+							"MozillaClient.test() failed: " + e);
+				} catch(Exception e) {
+					MessageDialog.openInformation(null, "Whack Plug-in", 
+							"MozillaClient.test() failed: " + e);
+				}
 			}
 		};
 		sampleAction.setText("Sample Action");
Index: src/org/mozilla/solid/wtp/helpers/ChromeURIResolver.java
===================================================================
RCS file: /cvs/solid/src/org.mozilla.solid.wtp/src/org/mozilla/solid/wtp/helpers/ChromeURIResolver.java,v
retrieving revision 1.1
diff -u -r1.1 ChromeURIResolver.java
--- src/org/mozilla/solid/wtp/helpers/ChromeURIResolver.java	11 Jul 2005 12:22:58 -0000	1.1
+++ src/org/mozilla/solid/wtp/helpers/ChromeURIResolver.java	17 Jul 2005 20:46:29 -0000
@@ -7,44 +7,104 @@
  *
  * Contributors:
  *    Axel Hecht <axel@pike.org> - initial API and implementation
+ *    Nickolay Ponomarev <asqueella@gmail.com> 
  *****************************************************************************/
 
 package org.mozilla.solid.wtp.helpers;
 
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.HashMap;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolverExtension;
+import org.mozilla.solid.wtp.sockets.MozillaClient;
 
+/**
+ * ChromeURIResolver is used to resolve chrome:// URIs to use real protocols,
+ * like file:// or jar:
+ */
 public class ChromeURIResolver implements URIResolverExtension {
 
-	public String resolve(IFile project, String baseLocation,
+	private static HashMap cache = new HashMap(); 
+	
+	public String resolve(IFile file, String baseLocation,
 			String publicId, String systemId) {
-		if (!systemId.startsWith("chrome:")) {
+
+		if(cache.containsKey(systemId))
+			return (String)cache.get(systemId);
+
+		if(!systemId.startsWith("chrome:")) {
 			return null;
 		}
-//		System.err.println("base: " + baseLocation);
-//		System.err.println("public: " + publicId);
-//		System.err.println("system: " + systemId);
-		String path, spec = null;
+
+		// parse the URI
+		ChromeURI uri;
 		try {
-			URI uri = new URI(systemId);
-			path = uri.getPath();
-		} catch (URISyntaxException e) {
-			System.err.println("Bad URL" + systemId);
+			uri = new ChromeURI(systemId);
+		} catch (ChromeURISyntaxException e) {
+			log("Bad chrome URI to resolve - " + systemId + ":" + e);
 			return null;
 		}
-		// XXX TODO: hook up chrome registry
-		int sep = path.lastIndexOf("/");
-		if (sep < 0)
-			System.err.println("bad chrome URL?");
+
+		String resolvedSpec = null;
+		// XXX: read the list of packages being edited as part of the project
+		// We probably need a separate project-wide plugin for handling such data?
+		if(uri.packageName.equals("solid")) {
+			// the package is a part of the current project.
+
+			log("Resolving " + uri.getSpec() + " to a file in this project.");
+			resolvedSpec = resolveToSibling(baseLocation, uri.path);
+		} 
+		else		
+		{
+			// the package is external to the current project. Ask Mozilla's
+			// chrome registry to resolve the URI. 
+			log("Resolving " + systemId + " as an external resource " +
+				"using running Mozilla instance.");
+			resolvedSpec = resolveWithMozilla(systemId);
+		}
+
+		// TODO: make the failure reason more visible to user 
+		log("resolved to " + resolvedSpec);
+		if(resolvedSpec != null)
+			cache.put(systemId, resolvedSpec);
+		return resolvedSpec;
+	}
+
+	/**
+	 * Tries to resolve the chrome URI by asking a running Mozilla process.
+	 * @param chromePath the chrome URI to resolve. 
+	 * @return resolved URI, or null if the URI couldn't be resolved. 
+	 */
+	String resolveWithMozilla(String chromePath) {
 		try {
-			spec = new URI(baseLocation).resolve(path.substring(sep+1)).toString();
+			return MozillaClient.resolveChromeURI(chromePath);
+		} catch(Exception e) {
+			log("resolveWithMozilla exception: " + e);
+		}
+		return null;
+	}
+
+	
+	
+	/**
+	 * Fallback: resolve to a file in the same folder as the referrer.
+	 * @param baseLocation the URI of the base (referrer) file
+	 * @param chromePath the chrome URI to resolve
+	 * @return resolved URI, or null if the URI couldn't be resolved (shouldn't happen). 
+	 */
+	String resolveToSibling(String baseLocation, String chromePath) {
+		int slash = chromePath.lastIndexOf("/");
+		String fileName = chromePath.substring(slash+1);
+		try {
+			return new URI(baseLocation).resolve(fileName).toString();
 		} catch (URISyntaxException e) {
-			System.err.println("Bad URL" + systemId);
+			log("Bad baseLocation? " + e.getMessage());
 		}
-		System.err.println("result: " + spec);
-		return spec;
+		return null;
 	}
 
+	private void log(String message) {
+		//System.err.println("ChromeURIResolver: " + message);
+	}
 }
Index: src/org/mozilla/solid/wtp/helpers/ChromeURI.java
===================================================================
RCS file: src/org/mozilla/solid/wtp/helpers/ChromeURI.java
diff -N src/org/mozilla/solid/wtp/helpers/ChromeURI.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/mozilla/solid/wtp/helpers/ChromeURI.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,170 @@
+/*****************************************************************************
+ * Copyright (c) 2005 Nickolay Ponomarev.
+ * 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:
+ *    Nickolay Ponomarev <asqueella@gmail.com> - initial API and implementation
+ *****************************************************************************/
+
+package org.mozilla.solid.wtp.helpers;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Represents an URI of form "chrome://package/provider/path/to/file, where:
+ *   "package" is the name of package, any string that keeps the URI well-formed.
+ *   "provider" is one of the following strings: "content", "locale", or "skin".
+ * 		  
+ */
+public class ChromeURI extends Object {
+
+	public String packageName;
+	public String provider;
+	public String path;
+	
+	/**
+	 * Creates a ChromeURI object from an URI object. 
+	 * @param uri The URI used to initialize the object.
+	 * @throws URISyntaxException
+	 */
+	public ChromeURI(URI uri) throws ChromeURISyntaxException {
+		super();
+		assign(uri);
+	}
+
+	/**
+	 * Creates a ChromeURI object by parsing a spec. 
+	 * @param spec The chrome URI spec used to initialize the object.
+	 * @throws URISyntaxException
+	 */
+	public ChromeURI(String spec) throws ChromeURISyntaxException {
+		super();
+		assign(spec);
+	}
+
+	/**
+	 * Creates a ChromeURI object from given components.
+	 * @param packageName
+	 * @param provider
+	 * @param path
+	 * @throws ChromeURISyntaxException
+	 */
+	public ChromeURI(String packageName, String provider, String path) throws ChromeURISyntaxException {
+		super();
+		this.packageName = packageName;
+		this.provider = provider;
+		this.path = path;
+		canonify();
+	}
+	
+	/**
+	 *   
+	 * @param spec  
+	 * @throws ChromeURISyntaxException
+	 */
+	public void assign(String spec) throws ChromeURISyntaxException {
+		try {
+			assign(new URI(spec));
+		} catch(URISyntaxException e) {
+			throw new ChromeURISyntaxException(e.toString());
+		}
+	}
+
+	/**
+	 *   
+	 * @param uri
+	 * @throws ChromeURISyntaxException
+	 */
+	public void assign(URI uri) throws ChromeURISyntaxException {
+		if(!uri.getScheme().equals("chrome"))
+			throw new ChromeURISyntaxException("chrome URIs must have 'chrome' scheme");
+		packageName = extractPackageName(uri);
+		String providerAndPath[] = extractProviderAndPath(uri);
+		provider = providerAndPath[0];
+		path = providerAndPath[1];
+		canonify();
+	}
+
+	/**
+	 * Converts the chrome URI into a canonical representation:
+	 * 	chrome://package/provider/
+	 * 		becomes 
+	 *  chrome://package/provider/package.ext
+	 * 
+	 * @throws ChromeURISyntaxException
+	 */
+	public void canonify() throws ChromeURISyntaxException {
+		if(packageName.length() == 0)
+			throw new ChromeURISyntaxException("Chrome URI missing package name: " + getSpec());
+		if(path.length() == 0) {
+			if(provider == "content") {
+				path += packageName + ".xul";
+			} else if(provider == "locale") {
+				path += packageName + ".dtd";
+			} else if(provider == "skin") {
+				path += packageName + ".css";
+			} else {
+				throw new ChromeURISyntaxException("Unknown provider type '" + 
+						provider + "' in URI " + getSpec());
+			}
+		}
+	}
+
+	/**
+	 * @returns the spec of this chrome URI.
+	 */
+	public String getSpec() {
+		return "chrome://" + packageName + "/" + provider + path;
+	}
+
+	/**
+	 * Extract the package name component of a chrome URI.
+	 * @param uri the chrome URI to examine.
+	 * @return the package name of the URI.  
+	 */
+	static public String extractPackageName(URI uri) {
+		return uri.getHost();
+	}
+	
+	/**
+	 * Extract the provider component of a chrome URI.
+	 * @param uri the chrome URI to examine.
+	 * @return the provider component of the URI.  
+	 */
+	static public String extractProvider(URI uri) throws ChromeURISyntaxException {
+		return extractProviderAndPath(uri)[0];
+	}
+
+	/**
+	 * Extract the path component of a chrome URI.
+	 * @param uri the chrome URI to examine.
+	 * @return the path component of the uri.  
+	 */
+	static public String extractPath(URI uri) throws ChromeURISyntaxException {
+		return extractProviderAndPath(uri)[1];
+	}
+
+	/**
+	 * A helper function which extracts both provider and path components of an
+	 * URI in one call.
+	 * @param uri the chrome URI to examine.
+	 * @return an array of two strings - first being the provider component, 
+	 * second the path. 
+	 * @throws ChromeURISyntaxException
+	 */
+	static private String[] extractProviderAndPath(URI uri) 
+	throws ChromeURISyntaxException 
+	{
+		String result[] = { uri.getPath(), "/" };
+		int slash = result[0].indexOf("/", 1);
+		if(slash > -1) {
+			result[1] = result[0].substring(slash);
+			result[0] = result[0].substring(1, slash);
+		}
+		return result;
+	}
+}
Index: src/org/mozilla/solid/wtp/helpers/ChromeURISyntaxException.java
===================================================================
RCS file: src/org/mozilla/solid/wtp/helpers/ChromeURISyntaxException.java
diff -N src/org/mozilla/solid/wtp/helpers/ChromeURISyntaxException.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/mozilla/solid/wtp/helpers/ChromeURISyntaxException.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,14 @@
+package org.mozilla.solid.wtp.helpers;
+
+/**
+ * ChromeURISyntaxException is an exception that is thrown when an invalid 
+ * chrome URL is encountered.
+ * @see ChromeURI
+ */
+public class ChromeURISyntaxException extends Exception {
+	private static final long serialVersionUID = -8696917904988820489L;
+	
+	public ChromeURISyntaxException(String message) {
+		super(message);
+	}
+}
Index: src/org/mozilla/solid/wtp/sockets/MozillaClient.java
===================================================================
RCS file: src/org/mozilla/solid/wtp/sockets/MozillaClient.java
diff -N src/org/mozilla/solid/wtp/sockets/MozillaClient.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/mozilla/solid/wtp/sockets/MozillaClient.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,188 @@
+/*****************************************************************************
+ * Copyright (c) 2005 Nickolay Ponomarev.
+ * 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:
+ *    Nickolay Ponomarev <asqueella@gmail.com> - initial API and implementation
+ *****************************************************************************/
+
+package org.mozilla.solid.wtp.sockets;
+
+import java.io.*;
+import java.net.*;
+import java.util.HashMap;
+
+
+/**
+ * MozillaClient is a class providing any functionality related to communicating
+ * with Mozilla. This works by opening a socket connection to a server, set up
+ * by our extension, which user must first install.
+ */
+public class MozillaClient  {
+	static public boolean test() throws SolidProtocolException, IOException {
+		return sendRequest("test 1").compareTo("test ok") == 0;
+	}
+
+	/**
+	 * Reads the document specified by URI. 
+	 * @param URI The chrome:// URI of the document to read.
+	 * @return The contents of the document.
+	 * @throws SolidProtocolException
+	 * @throws IOException
+	 */
+	static public String readChromeURI(String URI) throws SolidProtocolException, IOException {
+		return sendRequest("read " + URI);
+	}
+
+	/**
+	 * Resolves the specified chrome:// URI to a file:// or a jar: URI 
+	 * @param URI The chrome:// URI of the document to be resolved.
+	 * @return The resolved URI
+	 * @throws SolidProtocolException
+	 * @throws IOException
+	 */
+	static public String resolveChromeURI(String URI) throws SolidProtocolException, IOException {
+		return sendRequest("resolve " + URI);
+	}
+
+	// various keys for the response HashMap
+	static final String FAIL_REASON_KEY = "Fail-Reason";
+	static final String RESPONSE_KEY = "__response__";
+	static final String RESPONSE_LENGTH_KEY = "Response-Length";
+
+	/**
+	 * A simplified version of {@link #sendRequestEx(String)} which discards 
+	 * response headers (i.e. only returns the reply text). 
+	 * @param requestHeader
+	 * @return the reply text
+	 * @throws SolidProtocolException
+	 * @throws IOException
+	 */
+	static public String sendRequest(String requestHeader) throws SolidProtocolException, IOException {
+		HashMap response = sendRequestEx(requestHeader);
+		if(response.containsKey(RESPONSE_KEY))
+			return (String)response.get(RESPONSE_KEY);
+		return "";
+	}
+
+	/**
+	 * Sends the specified request and returns the HashMap of reply header. The 
+	 * result may contain the reply text at RESPONSE_KEY key.
+	 * @param requestHeader the request string, e.g. "resolve chrome://browser/locale/browser.dtd" 
+	 * @return
+	 * @throws SolidProtocolException
+	 * @throws IOException
+	 */
+	static public HashMap sendRequestEx(String requestHeader) throws SolidProtocolException, IOException {
+		Socket socket = connect();
+		try {
+			// Send request to the server
+			OutputStream out = socket.getOutputStream();
+			out.write(("SOLID/0.1\r\n" + requestHeader + "\r\n\r\n").getBytes());
+			out.flush();
+			//out.close();
+
+			// XXX: Not sure I'm closing all streams that need to be closed. 
+
+			// Receive and parse the response. Note that the input streams are
+			// blocking.
+			InputStreamReader ir = new InputStreamReader(socket.getInputStream());
+			BufferedReader rd = new BufferedReader(ir);
+			HashMap header = readHeaders(rd);
+			String responseLength = (String)header.get(RESPONSE_LENGTH_KEY);
+			if(responseLength == null)
+				return header;
+			int len = Integer.decode(responseLength).intValue();
+			String str = readFromStream(rd, len);
+			header.put(RESPONSE_KEY, str);
+			return header;
+		}
+		finally {
+			socket.close();
+		}
+	}
+
+	/**
+	 * Opens a socket connection to a localhost server.
+	 * @return the Socket object
+	 * @throws SolidProtocolException
+	 * @throws IOException
+	 */
+	static private Socket connect() throws SolidProtocolException, IOException {
+		final String server = "localhost";
+		final int port = 5031;
+		final int timeout = 2000;
+		Socket socket;
+		try {
+			socket = new Socket(server, port);
+			socket.setSoTimeout(timeout);
+		} catch(UnknownHostException e) {
+			throw new SolidProtocolException(e.toString());
+		}
+		return socket;
+	}
+
+	/**
+	 * Reads and parses the reply header (including the status line) from 
+	 * the specified BufferedReader.
+	 * @param rd where to read the header from.
+	 * @return a HashMap of key/value pairs from the reply header.
+	 * @throws SolidProtocolException
+	 * @throws IOException
+	 */
+	static private HashMap readHeaders(BufferedReader rd) throws SolidProtocolException, IOException {
+		HashMap header = new HashMap();
+		String responseLine = "";
+		do {
+			String str = rd.readLine();
+			if(str == null) {
+				throw new SolidProtocolException("Connection Reset");
+			} else if(str.equalsIgnoreCase("")) {
+				break;
+			} else if(responseLine.equalsIgnoreCase("")) {
+				responseLine = str;
+			} else {
+				final String SEP = ": ";
+				int sepIdx = str.indexOf(SEP);
+				if(sepIdx == -1)
+					throw new SolidProtocolException("Malformed header line: " + str);
+				String key = str.substring(0, sepIdx);
+				String value = str.substring(sepIdx + SEP.length());
+				header.put(key, value);
+			}
+		} while(true);
+
+		if(responseLine.compareTo("SOLID/0.1 FAIL") == 0) {
+			throw new SolidProtocolException("Server returned failure response: " + (String) header.get(FAIL_REASON_KEY));
+		} else if(responseLine.compareTo("SOLID/0.1 OK") != 0) {
+			throw new SolidProtocolException("Unexpected response-line: " + responseLine);
+		}
+		return header;
+	}
+
+	/**
+	 * Reads len bytes from rd. Does not return until len bytes have been read
+	 * or the connection has been reset. 
+	 * @param rd where to read the data from.
+	 * @param len how many bytes to read.
+	 * @return the read string.
+	 * @throws SolidProtocolException
+	 * @throws IOException
+	 */
+	static private String readFromStream(BufferedReader rd, int len) throws SolidProtocolException, IOException {
+		char s[] = new char[len];
+		int start = 0;
+		int read = 0;
+		while(start < len) {
+			read = rd.read(s, start, len-start);
+			if(read > -1)
+				start += read;
+			else
+				throw new SolidProtocolException("Connection Reset");
+		}
+		return String.valueOf(s); 
+	}
+}
Index: src/org/mozilla/solid/wtp/sockets/SolidProtocolException.java
===================================================================
RCS file: src/org/mozilla/solid/wtp/sockets/SolidProtocolException.java
diff -N src/org/mozilla/solid/wtp/sockets/SolidProtocolException.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/mozilla/solid/wtp/sockets/SolidProtocolException.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,17 @@
+package org.mozilla.solid.wtp.sockets;
+
+/**
+ * SolidProtocolException is thrown from MozillaClient class methods, when 
+ * a protocol-level error occurs while communicating with the server at 
+ * Mozilla's side. 
+ * @see MozillaClient
+ */
+public class SolidProtocolException extends Exception {
+
+	private static final long serialVersionUID = 247123874129753679L;
+
+	public SolidProtocolException (String message)
+	{
+		super(message);
+	}
+}

