Add a permissions model to the Chrome NPAPI plugin.

Permissions are stored in localstorage of the background page.
They can be changed by navigating to the extension's options page.
A page action indicates if the plugin permissions are good or bad for the current host.

Review at http://gwt-code-reviews.appspot.com/1084801


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9283 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/plugins/npapi/DevModeOptions/build.xml b/plugins/npapi/DevModeOptions/build.xml
new file mode 100644
index 0000000..da11701
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/build.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<project name="DevModeOptions" default="prebuilt" basedir=".">
+  <!-- Arguments to gwtc and devmode targets -->
+  <property name="gwt.args" value="" />
+
+  <!-- Configure path to GWT SDK -->
+  <property name="gwt.sdk" location="../../../build/staging/gwt-0.0.0" />
+
+  <path id="project.class.path">
+    <pathelement location="war/WEB-INF/classes"/>
+    <pathelement location="${gwt.sdk}/gwt-user.jar"/>
+    <fileset dir="${gwt.sdk}" includes="gwt-dev*.jar"/>
+    <!-- Add any additional non-server libs (such as JUnit) -->
+    <fileset dir="war/WEB-INF/lib" includes="**/*.jar"/>
+  </path>
+
+  <target name="javac" description="Compile java source to bytecode">
+    <mkdir dir="war/WEB-INF/classes"/>
+    <javac srcdir="src" includes="**" encoding="utf-8"
+        destdir="war/WEB-INF/classes"
+        source="1.5" target="1.5" nowarn="true"
+        debug="true" debuglevel="lines,vars,source">
+      <classpath refid="project.class.path"/>
+    </javac>
+    <copy todir="war/WEB-INF/classes">
+      <fileset dir="src" excludes="**/*.java"/>
+    </copy>
+  </target>
+
+  <target name="gwtc" depends="javac" description="GWT compile to JavaScript (production mode)">
+    <java failonerror="true" fork="true" classname="com.google.gwt.dev.Compiler">
+      <classpath>
+        <pathelement location="src"/>
+        <path refid="project.class.path"/>
+      </classpath>
+      <!-- add jvmarg -Xss16M or similar if you see a StackOverflowError -->
+      <jvmarg value="-Xmx256M"/>
+      <arg line="-war"/>
+      <arg value="war"/>
+      <!-- Additional arguments like -style PRETTY or -logLevel DEBUG -->
+      <arg line="${gwt.args}"/>
+      <arg value="com.google.gwt.devmodeoptions.DevModeOptions"/>
+    </java>
+  </target>
+
+  <target name="devmode" depends="javac" description="Run development mode">
+    <java failonerror="true" fork="true" classname="com.google.gwt.dev.DevMode">
+      <classpath>
+        <pathelement location="src"/>
+        <path refid="project.class.path"/>
+      </classpath>
+      <jvmarg value="-Xmx256M"/>
+      <arg value="-startupUrl"/>
+      <arg value="DevModeOptions.html"/>
+      <arg line="-war"/>
+      <arg value="war"/>
+      <!-- Additional arguments like -style PRETTY or -logLevel DEBUG -->
+      <arg line="${gwt.args}"/>
+      <arg value="com.google.devmodeoptions"/>
+    </java>
+  </target>
+
+  <target name="build" depends="gwtc" description="Build this project">
+  </target>
+
+  <target name="war" depends="build" description="Create a war file">
+    <zip destfile="DevModeOptions.war" basedir="war"/>
+  </target>
+
+  <target name="clean" depends="clean-prebuilt" description="Cleans this project">
+    <delete dir="war/WEB-INF/classes" failonerror="false" />
+    <delete dir="war/DevModeOptions" failonerror="false" />
+  </target>
+
+  <target name="clean-prebuilt" description="Clean the prebuilt copy">
+    <delete file="../prebuilt/gwt-dev-plugin/DevModeOptions.html" />
+    <delete dir="../prebuilt/gwt-dev-plugin/DevModeOptions" />
+  </target>
+
+  <target name="prebuilt" depends="build,clean-prebuilt" description="Copy compiled files into
+    the prebuilt crx directory">
+    <copy todir="../prebuilt/gwt-dev-plugin/DevModeOptions">
+      <fileset dir="war/DevModeOptions" excludes="**/*.java"/>
+    </copy>
+    <copy todir="../prebuilt/gwt-dev-plugin/">
+      <fileset dir="war/">
+        <include name="DevModeOptions.html" />
+      </fileset>
+    </copy>
+  </target>
+</project>
diff --git a/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/DevModeOptions.gwt.xml b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/DevModeOptions.gwt.xml
new file mode 100644
index 0000000..32a4aa4
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/DevModeOptions.gwt.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 0.0.999//EN" "http://google-web-toolkit.googlecode.com/svn/tags/0.0.999/distro-source/core/src/gwt-module.dtd">
+<module rename-to='DevModeOptions'>
+  <!-- Inherit the core Web Toolkit stuff.                        -->
+  <inherits name='com.google.gwt.user.User' />
+
+  <!-- Specify the app entry point class.                         -->
+  <entry-point class='com.google.gwt.devmodeoptions.client.DevModeOptions'/>
+
+  <!-- TARGETING WEBKIT ONLY -->
+  <set-property name='user.agent' value='safari' />
+
+  <!-- Specify the paths for translatable code                    -->
+  <source path='client'/>
+</module>
diff --git a/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/DevModeOptions.java b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/DevModeOptions.java
new file mode 100644
index 0000000..6ec7ae8
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/DevModeOptions.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.devmodeoptions.client;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.dom.client.StyleInjector;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.user.client.Window.Location;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FlexTable;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.RadioButton;
+import com.google.gwt.user.client.ui.RootLayoutPanel;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * The options page for configuring the set of hosts permitted to use the GWT
+ * Developer Plugin.
+ */
+public class DevModeOptions implements EntryPoint {
+
+  interface Binder extends UiBinder<Widget, DevModeOptions> {
+  }
+
+  private static final DevModeOptionsResources bundle = GWT.create(DevModeOptionsResources.class);
+
+  @UiField
+  Button addBtn;
+
+  @UiField
+  Label errorMessage;
+
+  @UiField
+  TextBox hostname;
+
+  JsArray<HostEntry> hosts;
+
+  @UiField
+  RadioButton includeNo;
+
+  @UiField
+  RadioButton includeYes;
+
+  @UiField
+  FlexTable savedHosts;
+
+  public void onModuleLoad() {
+    StyleInjector.inject(bundle.css().getText(), true);
+    RootLayoutPanel.get().add(
+        GWT.<Binder> create(Binder.class).createAndBindUi(this));
+
+    hosts = HostEntryStorage.get().getHostEntries();
+
+    addBtn.addClickHandler(new ClickHandler() {
+      public void onClick(ClickEvent event) {
+        addHost(HostEntry.create(hostname.getText(), includeYes.getValue()));
+      }
+    });
+
+    hostname.setFocus(true);
+    String host = Location.getParameter("host");
+    if (host != null) {
+      hostname.setText(host);
+    }
+
+    hostname.addKeyPressHandler(new KeyPressHandler() {
+      public void onKeyPress(KeyPressEvent event) {
+        if (event.getCharCode() == KeyCodes.KEY_ENTER) {
+          addHost(HostEntry.create(hostname.getText(), includeYes.getValue()));
+        }
+      }
+    });
+
+    savedHosts.setText(0, 0, "Host");
+    savedHosts.setText(0, 1, "Include/Exclude");
+    savedHosts.setText(0, 2, "Remove");
+    savedHosts.getCellFormatter().addStyleName(0, 0,
+        bundle.css().savedHostsHeading());
+    savedHosts.getCellFormatter().addStyleName(0, 0, bundle.css().textCol());
+
+    savedHosts.getCellFormatter().addStyleName(0, 1,
+        bundle.css().savedHostsHeading());
+
+    savedHosts.getCellFormatter().addStyleName(0, 2,
+        bundle.css().savedHostsHeading());
+
+    for (int i = 0; i < hosts.length(); i++) {
+      displayHost(hosts.get(i));
+    }
+  }
+
+  private void addHost(final HostEntry newHost) {
+    if (newHost.getUrl().length() == 0) {
+      return;
+    }
+
+    boolean alreadyExists = false;
+    for (int i = 0; i < hosts.length() && !alreadyExists; i++) {
+      if (hosts.get(i).isEqual(newHost)) {
+        alreadyExists = true;
+      }
+    }
+
+    if (alreadyExists) {
+      error("Cannot add duplicate host entry for " + newHost.getUrl());
+      return;
+    } else {
+      hosts.push(newHost);
+      clearError();
+    }
+    HostEntryStorage.get().saveEntries(hosts);
+
+    displayHost(newHost);
+
+    hostname.setText("");
+    hostname.setFocus(true);
+  }
+
+  private void clearError() {
+    errorMessage.setText("");
+  }
+
+  private void displayHost(final HostEntry newHost) {
+    int numRows = savedHosts.getRowCount();
+    int col = 0;
+    savedHosts.insertRow(numRows);
+    savedHosts.setText(numRows, col++, newHost.getUrl());
+    savedHosts.setText(numRows, col++, newHost.include() ? "Include"
+        : "Exclude");
+    if (newHost.include()) {
+      savedHosts.getCellFormatter().addStyleName(numRows, 0,
+          bundle.css().include());
+      savedHosts.getCellFormatter().addStyleName(numRows, 1,
+          bundle.css().include());
+    } else {
+      savedHosts.getCellFormatter().addStyleName(numRows, 0,
+          bundle.css().exclude());
+      savedHosts.getCellFormatter().addStyleName(numRows, 1,
+          bundle.css().exclude());
+    }
+
+    Button removeHostButton = new Button("x");
+    removeHostButton.addClickHandler(new ClickHandler() {
+      public void onClick(ClickEvent event) {
+        removeHost(newHost);
+      }
+    });
+    savedHosts.setWidget(numRows, col, removeHostButton);
+  }
+
+  private void error(String text) {
+    errorMessage.setText(text);
+  }
+
+  private void removeHost(HostEntry host) {
+    JsArray<HostEntry> newHosts = JavaScriptObject.createArray().cast();
+    for (int index = 0; index < hosts.length(); index++) {
+      if (hosts.get(index).isEqual(host)) {
+        savedHosts.removeRow(index + 1);
+      } else {
+        newHosts.push(hosts.get(index));
+      }
+    }
+
+    hosts = newHosts;
+    HostEntryStorage.get().saveEntries(hosts);
+  }
+
+}
diff --git a/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/DevModeOptions.ui.xml b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/DevModeOptions.ui.xml
new file mode 100644
index 0000000..555ce15
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/DevModeOptions.ui.xml
@@ -0,0 +1,52 @@
+<!-- Copyright 2010 Google Inc.                                             -->
+<!-- Licensed under the Apache License, Version 2.0 (the "License"); you    -->
+<!-- may not use this file except in compliance with the License. You may   -->
+<!-- may obtain a copy of the License at                                    -->
+<!--                                                                        -->
+<!-- http://www.apache.org/licenses/LICENSE-2.0                             -->
+<!--                                                                        -->
+<!-- Unless required by applicable law or agreed to in writing, software    -->
+<!-- distributed under the License is distributed on an "AS IS" BASIS,      -->
+<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        -->
+<!-- implied. License for the specific language governing permissions and   -->
+<!-- limitations under the License.                                         -->
+
+<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
+<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
+	xmlns:g="urn:import:com.google.gwt.user.client.ui">
+	<ui:with field='res'
+		type='com.google.gwt.devmodeoptions.client.DevModeOptionsResources' />
+	<g:HTMLPanel styleName="{res.css.mainPanel}">
+		<g:VerticalPanel>
+			<g:HorizontalPanel>
+				<g:Image resource='{res.gwt64}' styleName="{res.css.logo}" />
+				<g:HTML>
+					<h1> GWT Developer Plugin Options </h1>
+				</g:HTML>
+			</g:HorizontalPanel>
+
+			<g:Label styleName="{res.css.explanation}">
+				The GWT Developer Plugin will open a TCP/IP connection to an arbitrary
+				host/port at the request of a web page. To minimize security risks,
+				by default it will only connect to the local machine. To allow
+				cross-machine debugging, you can add exceptions here -- include the
+				exact host name of the web servers you will use for debugging, but
+				do not include any you do not trust.</g:Label>
+
+			<g:Label ui:field="errorMessage" styleName="{res.css.errorMessage}"/>
+			<g:HorizontalPanel>
+				<g:TextBox ui:field="hostname" styleName="{res.css.textBox}" />
+				<g:Button styleName="{res.css.important}" ui:field="addBtn">Add</g:Button>
+				<g:VerticalPanel>
+					<g:RadioButton name="include" ui:field="includeYes"
+						checked="true">Include</g:RadioButton>
+					<g:RadioButton name="include" ui:field="includeNo">Exclude</g:RadioButton>
+				</g:VerticalPanel>
+			</g:HorizontalPanel>
+
+			<g:FlexTable ui:field="savedHosts" styleName="{res.css.savedHosts}">
+			</g:FlexTable>
+		</g:VerticalPanel>
+	</g:HTMLPanel>
+
+</ui:UiBinder> 
diff --git a/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/DevModeOptionsResources.java b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/DevModeOptionsResources.java
new file mode 100644
index 0000000..3f17ced
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/DevModeOptionsResources.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.devmodeoptions.client;
+
+import com.google.gwt.resources.client.ClientBundle;
+import com.google.gwt.resources.client.CssResource;
+import com.google.gwt.resources.client.ImageResource;
+
+public interface DevModeOptionsResources extends ClientBundle {
+  
+  public interface Css extends CssResource {  
+    String errorMessage();
+  
+    String exclude();
+    
+    String explanation();
+    
+    String header();
+    
+    String important();
+    
+    String include();
+    
+    String logo();
+    
+    String mainPanel();
+    
+    String savedHosts();
+    
+    String savedHostsHeading();
+    
+    String textBox();
+    
+    String textCol();
+  }
+  
+  @Source("com/google/gwt/devmodeoptions/client/resources/DevModeOptions.css")
+  Css css();
+
+  @Source("com/google/gwt/devmodeoptions/client/resources/gwt128.png")
+  ImageResource gwt128();
+
+  @Source("com/google/gwt/devmodeoptions/client/resources/gwt16.png")
+  ImageResource gwt16();
+
+  @Source("com/google/gwt/devmodeoptions/client/resources/gwt32.png")
+  ImageResource gwt32();
+
+  @Source("com/google/gwt/devmodeoptions/client/resources/gwt48.png")
+  ImageResource gwt48();
+
+  @Source("com/google/gwt/devmodeoptions/client/resources/gwt64.png")
+  ImageResource gwt64();
+  
+  @Source("com/google/gwt/devmodeoptions/client/resources/warning.png")
+  ImageResource warning();
+}
diff --git a/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/HostEntry.java b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/HostEntry.java
new file mode 100644
index 0000000..672a783
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/HostEntry.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.devmodeoptions.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+
+public class HostEntry extends JavaScriptObject{  
+  
+  protected HostEntry() {}
+  
+  public static HostEntry create(String url, boolean include) {
+    HostEntry entry = JavaScriptObject.createObject().cast();
+    entry.setUrl(url);
+    entry.setInclude(include);
+    return entry;
+  }
+    
+  public final native String getUrl() /*-{
+    return this.url;
+  }-*/;
+  
+  public final native void setUrl(String url) /*-{
+    this.url = url;
+  }-*/;
+  
+  public final native void setInclude(boolean include) /*-{
+    this.include = include;
+  }-*/;
+  
+  public final native boolean include() /*-{
+    return this.include;
+  }-*/;
+
+  public final boolean isEqual(HostEntry host) {
+    return this.getUrl().equals(host.getUrl());
+  }
+}
diff --git a/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/HostEntryStorage.java b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/HostEntryStorage.java
new file mode 100644
index 0000000..6a7ddc0
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/HostEntryStorage.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.devmodeoptions.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArray;
+
+public class HostEntryStorage {
+  private static HostEntryStorage singleton = new HostEntryStorage();
+  private static final String HOST_ENTRY_KEY = "GWT_DEV_HOSTENTRY";
+  
+  public static HostEntryStorage get() {
+    return singleton;
+  }
+  
+  private final LocalStorage localStorage;
+  
+  private HostEntryStorage() {
+    localStorage = getLocalStorage();
+  }
+
+  private static native LocalStorage getLocalStorage() /*-{
+    return $wnd.localStorage;
+  }-*/;
+  
+  public JsArray<HostEntry> getHostEntries() {
+    JsArray<HostEntry> entries = localStorage.getItem(HOST_ENTRY_KEY).cast();
+    if (entries == null) {
+      return JavaScriptObject.createArray().cast();
+    } else {
+      return entries;
+    }
+  }
+  
+  public void saveEntries(JsArray<HostEntry> entries) {
+    localStorage.setItem(HOST_ENTRY_KEY, entries.cast());
+  }
+  
+}
diff --git a/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/LocalStorage.java b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/LocalStorage.java
new file mode 100644
index 0000000..9e6a6c2
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/LocalStorage.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.devmodeoptions.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * Simple wrapper around HTML5 <a
+ * href="http://dev.w3.org/html5/webstorage/#the-localstorage-attribute">local
+ * storage</a> API.
+ */
+public class LocalStorage extends JavaScriptObject {
+  protected LocalStorage() {
+  }
+
+  public final native void clear() /*-{
+    this.clear();
+  }-*/;
+
+  public final native JavaScriptObject getItem(String key) /*-{
+    return JSON.parse(this.getItem(key));
+  }-*/;
+
+  public final native String getKey(int index) /*-{
+    return this.key(index);
+  }-*/;
+
+  public final native int getLength() /*-{
+    return this.length;
+  }-*/;
+
+  public final native String getStringItem(String key) /*-{
+    return this.getItem(key);
+  }-*/;
+
+  public final native void removeItem(String key) /*-{
+    this.removeItem(key);
+  }-*/;
+
+  public final native void setItem(String key, JavaScriptObject dataObject) /*-{
+    // Note, as of FF3.6, gecko does not support storing an object (only strings).
+    this.setItem(key, JSON.stringify(dataObject));
+  }-*/;
+
+  public final native void setStringItem(String key, String dataString) /*-{
+    this.setItem(key, dataString);
+  }-*/;
+}
diff --git a/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/DevModeOptions.css b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/DevModeOptions.css
new file mode 100644
index 0000000..f42e7ad
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/DevModeOptions.css
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2010 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+@def TEXTWIDTH 30em;
+.savedHostsHeading {
+  font-weight: bold;
+}
+
+.savedHosts {
+  border: 2px solid silver;
+  padding: 2px;
+  margin-top: 1.5em;
+}
+
+.include {
+  color: blue;
+}
+
+.exclude {
+  color: IndianRed;
+}
+
+.errorMessage {
+  color: red;
+  font-weight: bold;
+}
+
+.important {
+  font-weight: bold;
+}
+
+.header {
+  font-size: large;
+  font-weight: bold;
+}
+
+.textBox {
+  width: TEXTWIDTH;
+}
+
+.textCol {
+  width: TEXTWIDTH;
+}
+
+.logo {
+  margin-right: 1em;
+}
+
+.mainPanel {
+  margin-left: 2em;
+  margin-top: 2em;
+  font-family: sans-serif;
+  font-size: small;
+  max-width: 50em;
+}
+
+.explanation {
+  margin-top: 1.5em;
+  margin-bottom: 1.5em;
+}
\ No newline at end of file
diff --git a/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt128.png b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt128.png
new file mode 100644
index 0000000..57e8e01
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt128.png
Binary files differ
diff --git a/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt16.png b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt16.png
new file mode 100644
index 0000000..f26096e
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt16.png
Binary files differ
diff --git a/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt32.png b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt32.png
new file mode 100644
index 0000000..7ba8270
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt32.png
Binary files differ
diff --git a/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt48.png b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt48.png
new file mode 100644
index 0000000..e8a4172
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt48.png
Binary files differ
diff --git a/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt64.png b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt64.png
new file mode 100644
index 0000000..922cc88
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/gwt64.png
Binary files differ
diff --git a/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/warning.png b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/warning.png
new file mode 100644
index 0000000..55a203b
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/src/com/google/gwt/devmodeoptions/client/resources/warning.png
Binary files differ
diff --git a/plugins/npapi/DevModeOptions/war/DevModeOptions.html b/plugins/npapi/DevModeOptions/war/DevModeOptions.html
new file mode 100644
index 0000000..d2132e8
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/war/DevModeOptions.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+  <head>
+    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+    
+    <title>GWT DevMode Options</title>
+    <script type="text/javascript" language="javascript" src="DevModeOptions/DevModeOptions.nocache.js"></script>
+  </head>
+
+  <body>
+  </body>
+</html>
diff --git a/plugins/npapi/DevModeOptions/war/WEB-INF/classes/marker b/plugins/npapi/DevModeOptions/war/WEB-INF/classes/marker
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/war/WEB-INF/classes/marker
diff --git a/plugins/npapi/DevModeOptions/war/WEB-INF/lib/marker b/plugins/npapi/DevModeOptions/war/WEB-INF/lib/marker
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/war/WEB-INF/lib/marker
diff --git a/plugins/npapi/DevModeOptions/war/WEB-INF/web.xml b/plugins/npapi/DevModeOptions/war/WEB-INF/web.xml
new file mode 100644
index 0000000..bd4f348
--- /dev/null
+++ b/plugins/npapi/DevModeOptions/war/WEB-INF/web.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xmlns="http://java.sun.com/xml/ns/javaee"
+xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
+http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
+</web-app>
diff --git a/plugins/npapi/Makefile b/plugins/npapi/Makefile
index 0a482f0..4aedc18 100644
--- a/plugins/npapi/Makefile
+++ b/plugins/npapi/Makefile
@@ -23,6 +23,7 @@
 # Mac puts multiple architectures into the same files
 TARGET_PLATFORM = Darwin-gcc3
 CHROME_PATH ?= /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome
+PLATFORM_DIR_SUFFIX = gwtDev.plugin/Contents/MacOS/
 else
 ifeq ($(OS),linux)
 RUN_PATH_FLAG = -rpath-link
@@ -49,6 +50,7 @@
 OBJ_OUTDIR        = build/$(TARGET_PLATFORM)
 EXTENSION_OUTDIR  = prebuilt/gwt-dev-plugin
 PLATFORM_DIR      = $(EXTENSION_OUTDIR)/$(TARGET_PLATFORM)
+PLATFORM_DIR_SUFFIX ?= ""
 
 INSTALLER_CRX     = prebuilt/gwt-dev-plugin.crx
 DLL               = $(OBJ_OUTDIR)/libGwtDevPlugin$(DLL_SUFFIX)
@@ -63,9 +65,9 @@
 endif
 
 .PHONY: default all crx lib common clean depend install install-platform \
-	versioned-files
+	versioned-files devmodeoptions
 
-default:: lib versioned-files
+default:: lib versioned-files devmodeoptions
 
 all:: common lib install-platform crx
 
@@ -112,7 +114,7 @@
 $(DLL): $(OBJS) $(COMMON)
 	$(CXX) -m$(FLAG32BIT) -o $@ $(OBJS) $(COMMON) $(DLLFLAGS) 
 	@mkdir -p $(PLATFORM_DIR)
-	cp $(DLL) $(PLATFORM_DIR)/
+	cp $(DLL) $(PLATFORM_DIR)/$(PLATFORM_DIR_SUFFIX)
 
 $(OBJ_OUTDIR)/%.o: %.cpp
 	$(CXX) $(CXXFLAGS) -c -o $@ -I. -I../common $<
@@ -123,6 +125,9 @@
 clean:
 	rm -rf build
 
+devmodeoptions:
+	(ant -f DevModeOptions/build.xml prebuilt)
+
 depend: $(OBJ_OUTDIR)
 	($(foreach src,$(SRCS),$(DEPEND)) true) >>Makefile
 #	makedepend -- $(CFLAGS) -- $(SRCS)
diff --git a/plugins/npapi/NPVariantWrapper.h b/plugins/npapi/NPVariantWrapper.h
index 91bcaaf..4e039e1 100644
--- a/plugins/npapi/NPVariantWrapper.h
+++ b/plugins/npapi/NPVariantWrapper.h
@@ -61,6 +61,22 @@
     return &variant;
   }
 
+  int isBoolean() const {
+    return isBoolean(variant);
+  }
+
+  static int isBoolean(const NPVariant& variant) {
+    return NPVARIANT_IS_BOOLEAN(variant);
+  }
+
+  bool getAsBoolean() const {
+    return getAsBoolean(variant);
+  }
+
+  static bool getAsBoolean(const NPVariant& variant) {
+    return NPVARIANT_TO_BOOLEAN(variant);
+  }
+
   int isInt() const {
     return isInt(variant);
   }
@@ -429,6 +445,10 @@
     NPVariantProxy::retain(variant); // does nothing, present for consistency
     return &variant;
   }
+  
+  bool isBoolean() const {
+    return NPVariantProxy::isBoolean(variant);
+  }
 
   int isInt() const {
     return NPVariantProxy::isInt(variant);
@@ -442,6 +462,10 @@
     return NPVariantProxy::isString(variant);
   }
   
+  bool getAsBoolean() const {
+    return NPVariantProxy::getAsBoolean(variant);
+  }
+
   int getAsInt() const {
     return NPVariantProxy::getAsInt(variant);
   }
diff --git a/plugins/npapi/ScriptableInstance.cpp b/plugins/npapi/ScriptableInstance.cpp
index 8b85d75..1e74d4d 100644
--- a/plugins/npapi/ScriptableInstance.cpp
+++ b/plugins/npapi/ScriptableInstance.cpp
@@ -21,10 +21,6 @@
 #include "ReturnMessage.h"
 #include "ServerMethods.h"
 #include "AllowedConnections.h"
-#ifdef _WINDOWS
-#include "Preferences.h"
-#include "AllowDialog.h"
-#endif
 
 #include "mozincludes.h"
 #include "scoped_ptr/scoped_ptr.h"
@@ -32,6 +28,10 @@
 
 using std::string;
 using std::endl;
+const static string BACKGROUND_PAGE_STR = "chrome-extension://jpjpnpmbddbjkfaccnmhnkdgjideieim/background.html";
+const static string UNKNOWN_STR = "unknown";
+const static string INCLUDE_STR = "include";
+const static string EXCLUDE_STR = "exclude";
 
 static inline string convertToString(const NPString& str) {
   return string(GetNPStringUTF8Characters(str), GetNPStringUTF8Length(str));
@@ -62,6 +62,12 @@
     _connectId(NPN_GetStringIdentifier("connect")),
     initID(NPN_GetStringIdentifier("init")),
     toStringID(NPN_GetStringIdentifier("toString")),
+    loadHostEntriesID(NPN_GetStringIdentifier("loadHostEntries")),
+    locationID(NPN_GetStringIdentifier("location")),
+    hrefID(NPN_GetStringIdentifier("href")),
+    urlID(NPN_GetStringIdentifier("url")),
+    includeID(NPN_GetStringIdentifier("include")),
+    getHostPermissionID(NPN_GetStringIdentifier("getHostPermission")),
     connectedID(NPN_GetStringIdentifier("connected")),
     statsID(NPN_GetStringIdentifier("stats")),
     gwtId(NPN_GetStringIdentifier("__gwt_ObjectId")),
@@ -178,7 +184,11 @@
 bool ScriptableInstance::hasMethod(NPIdentifier name) {
   Debug::log(Debug::Debugging) << "ScriptableInstance::hasMethod(name=" << NPN_UTF8FromIdentifier(name) << ")"
       << Debug::flush; 
-  if (name == _connectId || name == initID || name == toStringID) {
+  if (name == _connectId ||
+      name == initID ||
+      name == toStringID ||
+      name == loadHostEntriesID ||
+      name == getHostPermissionID) {
     return true;
   }
   return false;
@@ -200,6 +210,10 @@
     val += _channel->isConnected() ? 'Y' : 'N';
     val += ']';
     NPVariantProxy::assignFrom(*result, val);
+  } else if (name == loadHostEntriesID) {
+    loadHostEntries(args, argCount, result);
+  } else if (name == getHostPermissionID) {
+    getHostPermission(args, argCount, result);
   }
   return true;
 }
@@ -248,6 +262,81 @@
   result->type = NPVariantType_Bool;
 }
 
+string ScriptableInstance::getLocationHref() {
+  NPVariantWrapper locationVariant(*this);
+  NPVariantWrapper hrefVariant(*this);
+
+  // window.location
+  NPN_GetProperty(getNPP(), window, locationID, locationVariant.addressForReturn());
+  //window.location.href
+  NPN_GetProperty(getNPP(), locationVariant.getAsObject(), hrefID, hrefVariant.addressForReturn());
+
+  const NPString* locationHref = NPVariantProxy::getAsNPString(hrefVariant);
+  return convertToString(*locationHref);
+}
+
+
+void ScriptableInstance::loadHostEntries(const NPVariant* args, unsigned argCount, NPVariant* result) {
+  string locationHref = getLocationHref();
+  if (locationHref.compare(BACKGROUND_PAGE_STR) == 0) {
+    AllowedConnections::clearRules();
+    for (unsigned i = 0; i < argCount; ++i) {
+      //Get the host entry object {url: "somehost.net", include: true/false}
+      NPObject* hostEntry = NPVariantProxy::getAsObject(args[i]);
+      if (!hostEntry) {
+        Debug::log(Debug::Error) << "Got a host entry that is not an object.\n";
+        break;
+      }
+
+      //Get the url
+      NPVariantWrapper urlVariant(*this);
+      if (!NPN_GetProperty(getNPP(), hostEntry, urlID, urlVariant.addressForReturn()) ||
+          !urlVariant.isString()) {
+        Debug::log(Debug::Error) << "Got a host.url entry that is not a string.\n";
+        break;
+      }
+      const NPString* urlNPString = urlVariant.getAsNPString();
+      string urlString = convertToString(*urlNPString);
+
+      //Include/Exclude?
+      NPVariantWrapper includeVariant(*this);
+      if (!NPN_GetProperty(getNPP(), hostEntry, includeID, includeVariant.addressForReturn()) || 
+          !includeVariant.isBoolean()) {
+        Debug::log(Debug::Error) << "Got a host.include entry that is not a boolean.\n";
+        break;
+      }
+      bool include = includeVariant.getAsBoolean();
+      Debug::log(Debug::Info) << "Adding " << urlString << "(" << (include ? "include" : "exclude") << ")\n";
+      AllowedConnections::addRule(urlString, !include);
+    }
+  } else {
+    Debug::log(Debug::Error) << "ScriptableInstance::loadHostEntries called from outside the background page: " <<
+                             locationHref << "\n";
+  }
+}
+
+void ScriptableInstance::getHostPermission(const NPVariant* args, unsigned argCount, NPVariant* result) {
+  if (argCount != 1 || !NPVariantProxy::isString(args[0])) {
+    Debug::log(Debug::Error) << "ScriptableInstance::getHostPermission called with incorrect arguments.\n";
+  }
+
+  const NPString url = args[0].value.stringValue;
+  const string urlStr = convertToString(url);
+  bool allowed = false;
+  bool matches = AllowedConnections::matchesRule(urlStr, &allowed);
+  string retStr;
+
+  if (!matches) {
+    retStr = UNKNOWN_STR;
+  } else if (allowed) {
+    retStr = INCLUDE_STR;
+  } else {
+    retStr = EXCLUDE_STR;
+  }
+
+  NPVariantProxy(*this, *result) = retStr;
+}
+
 void ScriptableInstance::connect(const NPVariant* args, unsigned argCount, NPVariant* result) {
   if (argCount != 5 || !NPVariantProxy::isString(args[0])
       || !NPVariantProxy::isString(args[1])
@@ -263,7 +352,8 @@
     result->type = NPVariantType_Void;
     return;
   }
-  const NPString url = args[0].value.stringValue;
+  //ignore args[0]. Get the URL from window.location.href instead.
+  const string urlStr = getLocationHref();
   const NPString sessionKey = args[1].value.stringValue;
   const NPString hostAddr = args[2].value.stringValue;
   const NPString moduleName = args[3].value.stringValue;
@@ -272,27 +362,9 @@
       << ",sessionKey=" << NPVariantProxy::toString(args[1]) << ",host=" << NPVariantProxy::toString(args[2])
       << ",module=" << NPVariantProxy::toString(args[3]) << ",hostedHtmlVers=" << NPVariantProxy::toString(args[4])
       << ")" << Debug::flush;
-  const std::string urlStr = convertToString(url);
 
-#ifdef _WINDOWS
-  // TODO: platform-independent preferences storage
-  Preferences::loadAccessList();
-#endif
   bool allowed = false;
-  if (!AllowedConnections::matchesRule(urlStr, &allowed)) {
-#ifdef _WINDOWS
-    // TODO: platform-independent allow-connection dialog
-    bool remember = false;
-    allowed = AllowDialog::askUserToAllow(&remember);
-    if (remember) {
-      std::string host = AllowedConnections::getHostFromUrl(urlStr);
-      Preferences::addNewRule(host, !allowed);
-    }
-#elif 0
-    // WARNING: BIG SECURITY HOLE IF ENABLED!
-    allowed = true;
-#endif
-  }
+  AllowedConnections::matchesRule(urlStr, &allowed);
   if (!allowed) {
     BOOLEAN_TO_NPVARIANT(false, *result);
     result->type = NPVariantType_Bool;
@@ -356,7 +428,7 @@
   return id;
 }
 
-void ScriptableInstance::fatalError(HostChannel& channel, const std::string& message) {
+void ScriptableInstance::fatalError(HostChannel& channel, const string& message) {
   // TODO(jat): better error handling
   Debug::log(Debug::Error) << "Fatal error: " << message << Debug::flush;
 }
@@ -382,7 +454,7 @@
   }
 }
 
-void ScriptableInstance::loadJsni(HostChannel& channel, const std::string& js) {
+void ScriptableInstance::loadJsni(HostChannel& channel, const string& js) {
   NPString npScript;
   dupString(js.c_str(), npScript);
   NPVariantWrapper npResult(*this);
@@ -405,7 +477,7 @@
   NPObject* obj = localObjects.get(id);
   NPIdentifier propID;
   if (args[1].isString()) {
-    std::string propName = args[1].getString();
+    string propName = args[1].getString();
     propID = NPN_GetStringIdentifier(propName.c_str());
   } else {
     int propNum = args[1].getInt();
@@ -434,7 +506,7 @@
   NPObject* obj = localObjects.get(id);
   NPIdentifier propID;
   if (args[1].isString()) {
-    std::string propName = args[1].getString();
+    string propName = args[1].getString();
     propID = NPN_GetStringIdentifier(propName.c_str());
   } else {
     int propNum = args[1].getInt();
@@ -477,7 +549,7 @@
   }
   Debug::log(Debug::Error) << Debug::flush;
   // TODO(jat): should we create a real exception object?
-  std::string buf("unexpected invokeSpecial(");
+  string buf("unexpected invokeSpecial(");
   buf += static_cast<int>(dispatchId);
   buf += ")";
   returnValue->setString(buf);
@@ -485,7 +557,7 @@
 }
 
 bool ScriptableInstance::invoke(HostChannel& channel, const Value& thisRef,
-    const std::string& methodName, int numArgs, const Value* const args,
+    const string& methodName, int numArgs, const Value* const args,
     Value* returnValue) {
   Debug::log(Debug::Debugging) << "invokeJS(" << methodName << ", this=" 
       << thisRef.toString() << ", numArgs=" << numArgs << ")" << Debug::flush;
diff --git a/plugins/npapi/ScriptableInstance.h b/plugins/npapi/ScriptableInstance.h
index 2d03a67..a24e085 100644
--- a/plugins/npapi/ScriptableInstance.h
+++ b/plugins/npapi/ScriptableInstance.h
@@ -93,6 +93,13 @@
   const NPIdentifier _connectId;
   const NPIdentifier initID;
   const NPIdentifier toStringID;
+
+  const NPIdentifier loadHostEntriesID;
+  const NPIdentifier locationID;
+  const NPIdentifier hrefID;
+  const NPIdentifier urlID;
+  const NPIdentifier includeID;
+  const NPIdentifier getHostPermissionID;
   
   const NPIdentifier connectedID;
   const NPIdentifier statsID;
@@ -124,6 +131,8 @@
 
   void connect(const NPVariant* args, unsigned argCount, NPVariant* result);
   void init(const NPVariant* args, unsigned argCount, NPVariant* result);
+  void loadHostEntries(const NPVariant* args, unsigned argCount, NPVariant* result);
+  void getHostPermission(const NPVariant* args, unsigned argCount, NPVariant* result);
   
   Value clientMethod_getProperty(HostChannel& channel, int numArgs, const Value* const args);
   Value clientMethod_setProperty(HostChannel& channel, int numArgs, const Value* const args);
@@ -137,6 +146,7 @@
 
 private:
   std::string computeTabIdentity();
+  std::string getLocationHref();
 };
 
 #endif
diff --git a/plugins/npapi/VisualStudio/npapi-plugin.sln b/plugins/npapi/VisualStudio/npapi-plugin.sln
index 8b8af37..8d6e04a 100755
--- a/plugins/npapi/VisualStudio/npapi-plugin.sln
+++ b/plugins/npapi/VisualStudio/npapi-plugin.sln
@@ -1,6 +1,6 @@
 
-Microsoft Visual Studio Solution File, Format Version 9.00
-# Visual C++ Express 2005
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "npapi-plugin", "npapi-plugin.vcproj", "{6BF0C2CE-CB0C-421B-A67C-1E448371D24A}"
 EndProject
 Global
diff --git a/plugins/npapi/VisualStudio/npapi-plugin.vcproj b/plugins/npapi/VisualStudio/npapi-plugin.vcproj
index 74b9075..bb53d97 100755
--- a/plugins/npapi/VisualStudio/npapi-plugin.vcproj
+++ b/plugins/npapi/VisualStudio/npapi-plugin.vcproj
@@ -1,11 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <VisualStudioProject
 	ProjectType="Visual C++"
-	Version="8.00"
+	Version="9.00"
 	Name="npapi-plugin"
 	ProjectGUID="{6BF0C2CE-CB0C-421B-A67C-1E448371D24A}"
 	RootNamespace="npapi-plugin"
 	Keyword="Win32Proj"
+	TargetFrameworkVersion="131072"
 	>
 	<Platforms>
 		<Platform
@@ -39,7 +40,7 @@
 			<Tool
 				Name="VCCLCompilerTool"
 				Optimization="0"
-				AdditionalIncludeDirectories="&quot;$(ProjectDir)\..\..\platform\Win&quot;;&quot;$(ProjectDir)\..&quot;;&quot;$(ProjectDir)\..\..\common&quot;"
+				AdditionalIncludeDirectories="&quot;$(ProjectDir)\..&quot;;&quot;$(ProjectDir)\..\..\common&quot;"
 				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;FIREFOXPLUGIN_EXPORTS"
 				MinimalRebuild="true"
 				BasicRuntimeChecks="3"
@@ -69,6 +70,8 @@
 				GenerateDebugInformation="true"
 				ProgramDatabaseFile="$(IntDir)\$(TargetName).pdb"
 				SubSystem="2"
+				RandomizedBaseAddress="1"
+				DataExecutionPrevention="0"
 				ImportLibrary="$(IntDir)\$(TargetName).lib"
 				TargetMachine="1"
 			/>
@@ -91,9 +94,6 @@
 				Name="VCAppVerifierTool"
 			/>
 			<Tool
-				Name="VCWebDeploymentTool"
-			/>
-			<Tool
 				Name="VCPostBuildEventTool"
 			/>
 		</Configuration>
@@ -122,12 +122,14 @@
 				Name="VCCLCompilerTool"
 				Optimization="3"
 				EnableIntrinsicFunctions="true"
-				AdditionalIncludeDirectories="&quot;$(ProjectDir)\..\..\platform\Win&quot;;&quot;$(ProjectDir)\..&quot;;&quot;$(ProjectDir)\..\..\common&quot;"
+				FavorSizeOrSpeed="1"
+				AdditionalIncludeDirectories="&quot;$(ProjectDir)\..&quot;;&quot;$(ProjectDir)\..\..\common&quot;"
 				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;FIREFOXPLUGIN_EXPORTS"
 				ExceptionHandling="1"
 				RuntimeLibrary="2"
 				UsePrecompiledHeader="0"
 				WarningLevel="3"
+				WarnAsError="false"
 				Detect64BitPortabilityProblems="false"
 				DebugInformationFormat="3"
 			/>
@@ -153,6 +155,8 @@
 				SubSystem="2"
 				OptimizeReferences="2"
 				EnableCOMDATFolding="2"
+				RandomizedBaseAddress="1"
+				DataExecutionPrevention="0"
 				ImportLibrary="$(IntDir)\$(TargetName).lib"
 				TargetMachine="1"
 			/>
@@ -175,9 +179,6 @@
 				Name="VCAppVerifierTool"
 			/>
 			<Tool
-				Name="VCWebDeploymentTool"
-			/>
-			<Tool
 				Name="VCPostBuildEventTool"
 			/>
 		</Configuration>
@@ -327,18 +328,6 @@
 				</File>
 			</Filter>
 			<Filter
-				Name="win"
-				>
-				<File
-					RelativePath="..\..\platform\Win\AllowDialog.h"
-					>
-				</File>
-				<File
-					RelativePath="..\..\platform\Win\Preferences.h"
-					>
-				</File>
-			</Filter>
-			<Filter
 				Name="npapi"
 				>
 				<File
@@ -466,18 +455,6 @@
 					>
 				</File>
 			</Filter>
-			<Filter
-				Name="win"
-				>
-				<File
-					RelativePath="..\..\platform\Win\AllowDialog.cpp"
-					>
-				</File>
-				<File
-					RelativePath="..\..\platform\Win\Preferences.cpp"
-					>
-				</File>
-			</Filter>
 		</Filter>
 	</Files>
 	<Globals>
diff --git a/plugins/npapi/main.cpp b/plugins/npapi/main.cpp
index 557fe62..1e44452 100644
--- a/plugins/npapi/main.cpp
+++ b/plugins/npapi/main.cpp
@@ -19,15 +19,12 @@
 #include "scoped_ptr/scoped_ptr.h"
 
 #ifdef _WINDOWS
-// TODO: add platform-independent permission dialog
-#include "AllowDialog.h"
-
 #include <windows.h>
-
 BOOL APIENTRY DllMain(HMODULE hModule, DWORD ulReasonForCall, LPVOID lpReserved) {
-  AllowDialog::setHInstance(hModule);
   switch (ulReasonForCall) {
     case DLL_PROCESS_ATTACH:
+      DisableThreadLibraryCalls(hModule);
+      break;
     case DLL_THREAD_ATTACH:
     case DLL_THREAD_DETACH:
     case DLL_PROCESS_DETACH:
@@ -36,7 +33,6 @@
   return TRUE;
 }
 #endif
-
 extern "C" {
 
   static const NPNetscapeFuncs* browser;
@@ -242,8 +238,8 @@
   }
 
   int16 NPP_HandleEvent(NPP instance, void* event) {
-    Debug::log(Debug::Info) << "NPP_HandleEvent(instance=" << instance << ")" << Debug::flush;
-    return 1 ;
+    Debug::log(Debug::Spam) << "NPP_HandleEvent(instance=" << instance << ")" << Debug::flush;
+    return 0 ;
   }
 
   void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData) {
diff --git a/plugins/npapi/manifest-template.json b/plugins/npapi/manifest-template.json
index ecf565f..9c6a1b8 100644
--- a/plugins/npapi/manifest-template.json
+++ b/plugins/npapi/manifest-template.json
@@ -10,12 +10,16 @@
     "64": "gwt64.png",
     "128": "gwt128.png"
   },
+  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDi6RrEy9YllRLM8bGBcIEk5ECAG2z+8ngTz7wwzRAQJpOzDp1Alq8fQFjH0+dzxok4RFLrWKHjxGqvXzWyWyTEo2nY3ScHLN/RoANMs8pl9X6TygRyO+3naqZOtLCrYHfV49JKXnYoFVbY5eBVYxHYY3BHAOKJj9onyAM4UPmMzQIDAQAB",
   "background_page": "background.html",
-  "content_scripts": [
-    {
-      "matches": ["http://*/*", "https://*/*", "file:///*"],
-      "js": ["record_tab_id.js"]
-    }
+  "options_page" : "DevModeOptions.html",
+  "page_action" : {
+    "default_icon" : "gwt32.png",
+    "default_title" : "GWT Development Mode",
+    "default_popup" : "page_action.html"
+  },
+  "permissions" : [
+    "tabs"
   ],
   "plugins": [
     { "path": "WINNT_x86-msvc/npGwtDevPlugin.dll", "public": true },
diff --git a/plugins/npapi/prebuilt/gwt-dev-plugin.crx b/plugins/npapi/prebuilt/gwt-dev-plugin.crx
index 0a33eba..0852c4b 100644
--- a/plugins/npapi/prebuilt/gwt-dev-plugin.crx
+++ b/plugins/npapi/prebuilt/gwt-dev-plugin.crx
Binary files differ
diff --git a/plugins/npapi/prebuilt/gwt-dev-plugin/Darwin-gcc3/gwtDev.plugin/Contents/MacOS/libGwtDevPlugin.dylib b/plugins/npapi/prebuilt/gwt-dev-plugin/Darwin-gcc3/gwtDev.plugin/Contents/MacOS/libGwtDevPlugin.dylib
index 6e17d7b..3523020 100755
--- a/plugins/npapi/prebuilt/gwt-dev-plugin/Darwin-gcc3/gwtDev.plugin/Contents/MacOS/libGwtDevPlugin.dylib
+++ b/plugins/npapi/prebuilt/gwt-dev-plugin/Darwin-gcc3/gwtDev.plugin/Contents/MacOS/libGwtDevPlugin.dylib
Binary files differ
diff --git a/plugins/npapi/prebuilt/gwt-dev-plugin/Darwin-gcc3/libGwtDevPlugin.dylib b/plugins/npapi/prebuilt/gwt-dev-plugin/Darwin-gcc3/libGwtDevPlugin.dylib
deleted file mode 100755
index 98f3d3f..0000000
--- a/plugins/npapi/prebuilt/gwt-dev-plugin/Darwin-gcc3/libGwtDevPlugin.dylib
+++ /dev/null
Binary files differ
diff --git a/plugins/npapi/prebuilt/gwt-dev-plugin/Linux_x86-gcc3/libGwtDevPlugin.so b/plugins/npapi/prebuilt/gwt-dev-plugin/Linux_x86-gcc3/libGwtDevPlugin.so
index 2aec960..1294ca9 100755
--- a/plugins/npapi/prebuilt/gwt-dev-plugin/Linux_x86-gcc3/libGwtDevPlugin.so
+++ b/plugins/npapi/prebuilt/gwt-dev-plugin/Linux_x86-gcc3/libGwtDevPlugin.so
Binary files differ
diff --git a/plugins/npapi/prebuilt/gwt-dev-plugin/Linux_x86_64-gcc3/libGwtDevPlugin.so b/plugins/npapi/prebuilt/gwt-dev-plugin/Linux_x86_64-gcc3/libGwtDevPlugin.so
index 1f7949b..ce762bb 100755
--- a/plugins/npapi/prebuilt/gwt-dev-plugin/Linux_x86_64-gcc3/libGwtDevPlugin.so
+++ b/plugins/npapi/prebuilt/gwt-dev-plugin/Linux_x86_64-gcc3/libGwtDevPlugin.so
Binary files differ
diff --git a/plugins/npapi/prebuilt/gwt-dev-plugin/WINNT_x86-msvc/npGwtDevPlugin.dll b/plugins/npapi/prebuilt/gwt-dev-plugin/WINNT_x86-msvc/npGwtDevPlugin.dll
index 2631415..dd1522a 100755
--- a/plugins/npapi/prebuilt/gwt-dev-plugin/WINNT_x86-msvc/npGwtDevPlugin.dll
+++ b/plugins/npapi/prebuilt/gwt-dev-plugin/WINNT_x86-msvc/npGwtDevPlugin.dll
Binary files differ
diff --git a/plugins/npapi/prebuilt/gwt-dev-plugin/background.html b/plugins/npapi/prebuilt/gwt-dev-plugin/background.html
index 0bca1a5..2569678 100644
--- a/plugins/npapi/prebuilt/gwt-dev-plugin/background.html
+++ b/plugins/npapi/prebuilt/gwt-dev-plugin/background.html
@@ -1,10 +1,65 @@
 <html>
 <head>
-<script>
-  chrome.extension.onConnect.addListener(function(port) {
-    // Tell my caller his tabId
-    port.postMessage( { name:"tabId", tabId:port.tab.id } );
-  });
-</script>
 </head>
+
+<embed id="pluginEmbed" type="application/x-gwt-hosted-mode" width="10"
+height="10">
+</embed>
+
+
+<script>
+var plugin = document.getElementById('pluginEmbed');
+var disabledIcon = 'gwt32-gray.png';
+var enabledIcon = 'gwt32.png';
+
+
+function getHostFromUrl(url) {
+  var hostname = '';
+  var idx = url.indexOf('://');
+  if (idx >= 0) {
+    idx += 3;
+    hostname = url.substring(idx);
+  }
+  idx = hostname.indexOf('/');
+  if (idx >= 0) {
+    hostname = hostname.split('/')[0];
+  }
+  idx = hostname.indexOf(':');
+  if (idx >= 0) {
+    hostname = hostname.split(':')[0];
+  }
+  return hostname;
+}
+
+function devModeTabListener(tabId, changeInfo, tab) {
+  var search = tab.url.slice(tab.url.indexOf('?'));
+  if (search.indexOf('gwt.codesvr=') >= 0 || search.indexOf('gwt.hosted=') >= 0) {
+    var permission = plugin.getHostPermission(tab.url);
+    var host = getHostFromUrl(tab.url);
+    var popup = 'page_action.html';
+    var icon = null;
+    console.log("got permission " + permission + " for host " + host);
+
+    if (permission == 'include') {
+      icon = enabledIcon;
+    } else if (permission == 'exclude') {
+      icon = disabledIcon;
+    } else if (permission == 'unknown') {
+      icon = disabledIcon;
+    }
+    popup += "?permission=" + permission + "&host=" + host;
+    chrome.pageAction.setIcon({'tabId' : tabId, 'path' : icon});
+    chrome.pageAction.setPopup({'tabId' : tabId, 'popup' : popup});
+    chrome.pageAction.show(tabId);
+
+    var hostEntries = window.localStorage.getItem('GWT_DEV_HOSTENTRY') || [];
+    console.log("loading hostentries: " + hostEntries);
+    plugin.loadHostEntries.apply(plugin, JSON.parse(hostEntries));
+  } else {
+    chrome.pageAction.hide(tabId);
+  }
+};
+
+chrome.tabs.onUpdated.addListener(devModeTabListener);
+</script>
 </html>
diff --git a/plugins/npapi/prebuilt/gwt-dev-plugin/gwt32-gray.png b/plugins/npapi/prebuilt/gwt-dev-plugin/gwt32-gray.png
new file mode 100644
index 0000000..d02689e
--- /dev/null
+++ b/plugins/npapi/prebuilt/gwt-dev-plugin/gwt32-gray.png
Binary files differ
diff --git a/plugins/npapi/prebuilt/gwt-dev-plugin/manifest.json b/plugins/npapi/prebuilt/gwt-dev-plugin/manifest.json
index 0f1cdf0..f425fc2 100644
--- a/plugins/npapi/prebuilt/gwt-dev-plugin/manifest.json
+++ b/plugins/npapi/prebuilt/gwt-dev-plugin/manifest.json
@@ -1,6 +1,6 @@
 {
   "name": "GWT Developer Plugin",
-  "version": "1.0.9119",
+  "version": "1.0.9274",
   "description": "A plugin to enable debugging with GWT's Development Mode",
   "update_url": "https://dl-ssl.google.com/gwt/plugins/chrome/updates.xml",
   "icons": {
@@ -10,12 +10,16 @@
     "64": "gwt64.png",
     "128": "gwt128.png"
   },
+  "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDi6RrEy9YllRLM8bGBcIEk5ECAG2z+8ngTz7wwzRAQJpOzDp1Alq8fQFjH0+dzxok4RFLrWKHjxGqvXzWyWyTEo2nY3ScHLN/RoANMs8pl9X6TygRyO+3naqZOtLCrYHfV49JKXnYoFVbY5eBVYxHYY3BHAOKJj9onyAM4UPmMzQIDAQAB",
   "background_page": "background.html",
-  "content_scripts": [
-    {
-      "matches": ["http://*/*", "https://*/*", "file:///*"],
-      "js": ["record_tab_id.js"]
-    }
+  "options_page" : "DevModeOptions.html",
+  "page_action" : {
+    "default_icon" : "gwt32.png",
+    "default_title" : "GWT Development Mode",
+    "default_popup" : "page_action.html"
+  },
+  "permissions" : [
+    "tabs"
   ],
   "plugins": [
     { "path": "WINNT_x86-msvc/npGwtDevPlugin.dll", "public": true },
diff --git a/plugins/npapi/prebuilt/gwt-dev-plugin/page_action.html b/plugins/npapi/prebuilt/gwt-dev-plugin/page_action.html
new file mode 100644
index 0000000..f891515
--- /dev/null
+++ b/plugins/npapi/prebuilt/gwt-dev-plugin/page_action.html
@@ -0,0 +1,47 @@
+<head>
+  <style>
+    body {
+      width: 20em;
+    }
+  </style>
+  <script>
+    function getParam(key) {
+      var idx = window.location.search.indexOf(key + "=");
+      var value = '';
+      if (idx >= 0) {
+        idx += key.length + 1;
+        value = window.location.search.substring(idx).split('&')[0];
+      }
+      return value;
+    }
+
+    function init() {
+      var permission = getParam('permission');
+      var host = getParam('host');
+      var message='';
+
+      if (permission == 'include') {
+        message = 'The host at ' + host + ' is allowed to use the plugin';
+      } else if (permission == 'exclude') {
+        message = 'The host at ' + host + ' has been been blacklisted.';
+      } else if (permission == 'unknown') {
+        message = 'The host at ' + host + ' is unknown to the plugin.';
+      }
+
+      document.getElementById('message').innerText = message;
+    }
+
+    function updateConfiguration() {
+      var url = 'DevModeOptions.html?host=' + getParam('host');
+      url = chrome.extension.getURL(url);
+      chrome.tabs.create({'url' : url});
+    }
+  </script>
+</head>
+<body onload='javascript:init()'>
+  <h3>GWT Developer Plugin</h3>
+  <div id='message'></div>
+  <br/>
+  <button onclick= "updateConfiguration()">Update Your Configuration</button>
+  </p>
+</body>
diff --git a/plugins/npapi/prebuilt/gwt-dev-plugin/record_tab_id.js b/plugins/npapi/prebuilt/gwt-dev-plugin/record_tab_id.js
deleted file mode 100644
index 78c9e73..0000000
--- a/plugins/npapi/prebuilt/gwt-dev-plugin/record_tab_id.js
+++ /dev/null
@@ -1,13 +0,0 @@
-if (window != top && location.href.indexOf("hosted.html") >= 0) {
-  var port = chrome.extension.connect();
-  port.onMessage.addListener(function(msg) {
-    if (msg.name == "tabId") {
-      var doc = window.document;
-      var div = document.createElement("div");
-      div.id = "$__gwt_tab_id";
-      div.textContent = "" + msg.tabId;
-      doc.body.appendChild(div);
-      // console.log("record_tab_id.js " + msg.tabId);
-    }
-  });
-}
diff --git a/plugins/npapi/prebuilt/gwt-dev-plugin/warning.png b/plugins/npapi/prebuilt/gwt-dev-plugin/warning.png
new file mode 100644
index 0000000..55a203b
--- /dev/null
+++ b/plugins/npapi/prebuilt/gwt-dev-plugin/warning.png
Binary files differ
diff --git a/plugins/platform/Win/AllowDialog.cpp b/plugins/platform/Win/AllowDialog.cpp
deleted file mode 100644
index 6e5672b..0000000
--- a/plugins/platform/Win/AllowDialog.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2009 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-#include "AllowDialog.h"
-#include "Debug.h"
-#include "resource.h"
-
-HINSTANCE AllowDialog::hInstance;
-
-static BOOL CALLBACK allowDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) {
-  if (message != WM_COMMAND) {
-    return false;
-  }
-  bool allowed;
-  switch (LOWORD(wParam)) {
-    case IDCANCEL:
-      allowed = false;
-      break;
-    case IDC_ALLOW_BUTTON:
-      allowed = true;
-      break;
-    default:
-      // ignore anything but buttons which close the dialog
-      return false;
-  }
-  bool remember = IsDlgButtonChecked(hwndDlg, IDC_REMEMBER_CHECKBOX) == BST_CHECKED;
-  int returnVal = (allowed ? 1 : 0) + (remember ? 2 : 0);
-  EndDialog(hwndDlg, (INT_PTR) returnVal);
-  return true;
-}
-
-void AllowDialog::setHInstance(HINSTANCE hInstance) {
-  AllowDialog::hInstance = hInstance;
-}
-
-bool AllowDialog::askUserToAllow(bool* remember) {
-  int result = (int) DialogBox(hInstance, MAKEINTRESOURCE(IDD_ALLOW_DIALOG),
-      NULL, (DLGPROC) allowDialogProc);
-  *remember = (result & 2) != 0;
-  return (result & 1) != 0;
-}
diff --git a/plugins/platform/Win/AllowDialog.h b/plugins/platform/Win/AllowDialog.h
deleted file mode 100644
index 93f9438..0000000
--- a/plugins/platform/Win/AllowDialog.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef _H_AllowDialog
-#define _H_AllowDialog
-/*
- * Copyright 2009 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-#ifdef _WINDOWS
-#include <windows.h>
-#include <winnt.h>
-#include <windef.h>
-
-class AllowDialog {
-public:
-  static void setHInstance(HINSTANCE hInstance);
-
-  /**
-   * Ask the user if a connection should be allowed.
-   *
-   * @param remember *remember is set to true if the user asked us to remember this decision,
-   *     false otherwise
-   * @return return true if this connection should be allowed
-   */
-  static bool askUserToAllow(bool* remember);
-
-private:
-  static HINSTANCE hInstance;
-};
-#endif
-
-#endif
diff --git a/plugins/platform/Win/Preferences.cpp b/plugins/platform/Win/Preferences.cpp
deleted file mode 100644
index 7eeb381..0000000
--- a/plugins/platform/Win/Preferences.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-#include <windows.h>
-#include <winnt.h>
-#include <winreg.h>
-#include "Debug.h"
-#include "Preferences.h"
-#include "AllowedConnections.h"
-
-#define REG_ACCESS_LIST "SOFTWARE\\Google\\Google Web Toolkit\\gwt-dev-plugin.accessList"
-
-/**
- * Return a null-terminated string containing the access list.
- *
- * @param HKEY registry key for the access list value
- * @return null-terminated string containing the access list (an empty string
- *     if the value does not exist) -- caller is responsible for freeing with
- *     delete[]
- */
-static char* getAccessList(HKEY keyHandle) {
-  char *buf;
-  DWORD len = 512;
-  while(true) {
-    buf = new char[len];
-    int cc = RegQueryValueExA(keyHandle, NULL, 0, NULL, (LPBYTE) buf, &len);
-    if (cc == ERROR_SUCCESS) {
-      break;
-    } else if (cc == ERROR_FILE_NOT_FOUND) {
-      // special handling if the value doesn't exist
-      len = 0;
-      break;
-    } else if (cc != ERROR_MORE_DATA) {
-      // log unexpected errors
-      Debug::log(Debug::Error) << "Unable to load access list from registry: "
-          << cc << Debug::flush;
-      len = 0;
-      break;
-    }
-    // Buffer wasn't big enough, so make it bigger and try again
-    delete [] buf;
-    len *= 2;
-  }
-  buf[len] = 0;
-  return buf;
-}
-
-void Preferences::addNewRule(const std::string& pattern, bool exclude) {
-  HKEY keyHandle;
-  if (RegCreateKeyExA(HKEY_CURRENT_USER, REG_ACCESS_LIST, 0, 0,
-      REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &keyHandle, NULL)
-      != ERROR_SUCCESS) {
-    return;
-  }
-  char *buf = getAccessList(keyHandle);
-  std::string pref(buf);
-  delete [] buf;
-  if (pref.length() > 0) {
-    pref += ',';
-  }
-  if (exclude) {
-    pref += '!';
-  }
-  pref += pattern;
-  int cc = RegSetValueExA(keyHandle, NULL, 0, REG_SZ, (LPBYTE) pref.c_str(),
-      pref.length() + 1);
-  if (cc != ERROR_SUCCESS) {
-      Debug::log(Debug::Error) << "Unable to store access list in registry: "
-          << cc << Debug::flush;
-  }
-  RegCloseKey(keyHandle);
-}
-
-void Preferences::loadAccessList() {
-  // TODO(jat): can Reg* routines throw exceptions?  If so, we need to make
-  // this exception safe about closing the key hendle and freeing the buffer.
-  HKEY keyHandle;
-  if (RegCreateKeyExA(HKEY_CURRENT_USER, REG_ACCESS_LIST, 0, 0,
-      REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &keyHandle, NULL)
-      != ERROR_SUCCESS) {
-    return;
-  }
-  char *buf = getAccessList(keyHandle);
-  AllowedConnections::initFromAccessList(buf);
-  delete [] buf;
-  RegCloseKey(keyHandle);
-}
diff --git a/plugins/platform/Win/Preferences.h b/plugins/platform/Win/Preferences.h
deleted file mode 100644
index f4fc49b..0000000
--- a/plugins/platform/Win/Preferences.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef _H_Preferences
-#define _H_Preferences
-/*
- * Copyright 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-#include <string>
-
-/**
- * Deal with getting/storing/updating preferences in the Windows registry.
- */
-class Preferences {
-private:
-  // prevent instantiation
-  Preferences() {}
-
-public:
-  static void loadAccessList();
-  static void addNewRule(const std::string& pattern, bool exclude);
-};
-
-#endif