summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDariusz Luksza <dariusz@luksza.org>2012-10-10 11:10:09 +0200
committerShawn O. Pearce <sop@google.com>2012-10-21 16:50:03 -0700
commitcd4fc1c2391434edb3da7c8f5f1559e512b13423 (patch)
tree429896f05592088031b3c0c4e08b7172b6745f84
parentbc16fa2f3f4b5d653be0b064218d325db1f60112 (diff)
JavaScipt and GWT based UI plugin support
Adds support for extending Gerrit's web UI using either pure JavaScript or GWT compiled with a modified CrossSiteIframeLinker. To add code to the web UI plugins should bind WebUiPlugin to either GwtPlugin or JavaScriptPlugin in their plugin's Web-Module. This tells the UI bootstrap code where to find additional JavaScript to inject into the page before displaying content. For a GWT based plugin: * com.google.gerrit.client.Plugin class should be extended, * inheritance from com.google.gerrit.Plugin should be added to gwt.xml, * subclass of client.Plugin should be set as entry point, * dependency from com.google.gerrit/gerrit-plugin-gwtui should be added to pom.xml, * webappDirectory option for gwt-maven-plugin should be set to ${project.build.directory}/classes/static For JavaScript based plugins the source code should be put into the static/ directory of the plugin's JAR file. Currently there are no extension points for either JS or GWT plugins to interact with. Some will be added in a future commit. With this commit only simple modification of the UI is possible, for example calling window.alert() or adding extra elements to page contents based on the existing element ids. Change-Id: I3558991ba4a1477d6cde64eb0a761a7e4c399ec3 Signed-off-by: Dariusz Luksza <dariusz@luksza.org>
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java3
-rw-r--r--gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/GwtPlugin.java33
-rw-r--r--gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/JavaScriptPlugin.java33
-rw-r--r--gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/WebUiPlugin.java54
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java56
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java3
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties3
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java3
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java4
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java63
-rw-r--r--gerrit-plugin-gwtui/.gitignore6
-rw-r--r--gerrit-plugin-gwtui/.settings/org.eclipse.core.resources.prefs5
-rw-r--r--gerrit-plugin-gwtui/.settings/org.eclipse.jdt.core.prefs295
-rw-r--r--gerrit-plugin-gwtui/.settings/org.eclipse.jdt.ui.prefs7
-rw-r--r--gerrit-plugin-gwtui/pom.xml80
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/Plugin.gwt.xml23
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/client/Plugin.java29
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/linker/GerritPluginLinker.java31
-rw-r--r--gerrit-plugin-gwtui/src/main/java/com/google/gerrit/rebind/PluginGenerator.java95
-rw-r--r--gerrit-plugin-gwtui/src/main/resources/com/google/gerrit/linker/computeUrlForPluginResource.js3
-rw-r--r--pom.xml3
21 files changed, 793 insertions, 39 deletions
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
index f991f4c118..fc7f92d76a 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
@@ -17,6 +17,8 @@ package com.google.gerrit.common.data;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountDiffPreference;
+import java.util.List;
+
/** Data sent as part of the host page, to bootstrap the UI. */
public class HostPageData {
public Account account;
@@ -24,6 +26,7 @@ public class HostPageData {
public String xsrfToken;
public GerritConfig config;
public Theme theme;
+ public List<String> plugins;
public static class Theme {
public String backgroundColor;
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/GwtPlugin.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/GwtPlugin.java
new file mode 100644
index 0000000000..e741a3f94d
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/GwtPlugin.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// 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.gerrit.extensions.webui;
+
+/** Configures a web UI plugin compiled using GWT. */
+public class GwtPlugin extends WebUiPlugin {
+ private final String moduleName;
+
+ /**
+ * @param moduleName name of GWT module. The resource
+ * {@code static/$MODULE/$MODULE.nocache.js} will be used.
+ */
+ public GwtPlugin(String moduleName) {
+ this.moduleName = moduleName;
+ }
+
+ @Override
+ public String getJavaScriptResourcePath() {
+ return String.format("static/%s/%s.nocache.js", moduleName, moduleName);
+ }
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/JavaScriptPlugin.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/JavaScriptPlugin.java
new file mode 100644
index 0000000000..89a4f33595
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/JavaScriptPlugin.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// 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.gerrit.extensions.webui;
+
+/** Configures a web UI plugin written using JavaScript. */
+public class JavaScriptPlugin extends WebUiPlugin {
+ private final String fileName;
+
+ /**
+ * @param fileName of JavaScript source file under {@code static/}
+ * subdirectory within the plugin's JAR.
+ */
+ public JavaScriptPlugin(String fileName) {
+ this.fileName = fileName;
+ }
+
+ @Override
+ public String getJavaScriptResourcePath() {
+ return "static/" + fileName;
+ }
+}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/WebUiPlugin.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/WebUiPlugin.java
new file mode 100644
index 0000000000..0208229bee
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/webui/WebUiPlugin.java
@@ -0,0 +1,54 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// 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.gerrit.extensions.webui;
+
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.inject.Inject;
+
+/**
+ * Specifies JavaScript to dynamically load into the web UI.
+ *
+ * @see GwtPlugin
+ * @see JavaScriptPlugin
+ */
+public abstract class WebUiPlugin {
+ public static final GwtPlugin gwt(String moduleName) {
+ return new GwtPlugin(moduleName);
+ }
+
+ public static final JavaScriptPlugin js(String scriptName) {
+ return new JavaScriptPlugin(scriptName);
+ }
+
+ private String pluginName;
+
+ /** @return installed name of the plugin that provides this UI feature. */
+ public final String getPluginName() {
+ return pluginName;
+ }
+
+ @Inject
+ void setPluginName(@PluginName String pluginName) {
+ this.pluginName = pluginName;
+ }
+
+ /** @return path to initialization script within the plugin's JAR. */
+ public abstract String getJavaScriptResourcePath();
+
+ @Override
+ public String toString() {
+ return getJavaScriptResourcePath();
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
index 31115d4d97..9276ba1f16 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
@@ -42,9 +42,11 @@ import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.AccountGeneralPreferences;
import com.google.gerrit.reviewdb.client.AuthType;
+import com.google.gwt.core.client.Callback;
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.ScriptInjector;
import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
@@ -68,9 +70,12 @@ import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwtexpui.clippy.client.CopyableLabel;
import com.google.gwtexpui.user.client.UserAgent;
import com.google.gwtexpui.user.client.ViewSite;
+import com.google.gwtjsonrpc.client.CallbackHandle;
import com.google.gwtjsonrpc.client.JsonDefTarget;
import com.google.gwtjsonrpc.client.JsonUtil;
import com.google.gwtjsonrpc.client.XsrfManager;
+import com.google.gwtjsonrpc.client.impl.ResultDeserializer;
+import com.google.gwtjsonrpc.common.AsyncCallback;
import com.google.gwtorm.client.KeyUtil;
import java.util.ArrayList;
@@ -371,6 +376,7 @@ public class Gerrit implements EntryPoint {
final HostPageDataService hpd = GWT.create(HostPageDataService.class);
hpd.load(new GerritCallback<HostPageData>() {
+ @Override
public void onSuccess(final HostPageData result) {
Document.get().getElementById("gerrit_hostpagedata").removeFromParent();
myConfig = result.config;
@@ -383,7 +389,7 @@ public class Gerrit implements EntryPoint {
myAccountDiffPref = result.accountDiffPref;
applyUserPreferences();
}
- onModuleLoad2();
+ onModuleLoad2(result);
}
});
}
@@ -465,7 +471,7 @@ public class Gerrit implements EntryPoint {
btmmenu.add(poweredBy);
}
- private void onModuleLoad2() {
+ private void onModuleLoad2(HostPageData hpd) {
RESOURCES.gwt_override().ensureInjected();
RESOURCES.css().ensureInjected();
@@ -543,6 +549,7 @@ public class Gerrit implements EntryPoint {
refreshMenuBar();
History.addValueChangeHandler(new ValueChangeHandler<String>() {
+ @Override
public void onValueChange(final ValueChangeEvent<String> event) {
display(event.getValue());
}
@@ -558,7 +565,50 @@ public class Gerrit implements EntryPoint {
if (signInAnchor != null) {
signInAnchor.setHref(loginRedirect(token));
}
- display(token);
+ loadPlugins(hpd, token);
+ }
+
+ private void loadPlugins(HostPageData hpd, final String token) {
+ if (hpd.plugins != null) {
+ for (final String url : hpd.plugins) {
+ ScriptInjector.fromUrl(url)
+ .setWindow(ScriptInjector.TOP_WINDOW)
+ .setCallback(new Callback<Void, Exception>() {
+ @Override
+ public void onSuccess(Void result) {
+ }
+
+ @Override
+ public void onFailure(Exception reason) {
+ ErrorDialog d = new ErrorDialog(reason);
+ d.setTitle(M.pluginFailed(url));
+ d.center();
+ }
+ }).inject();
+ }
+ }
+
+ CallbackHandle<Void> cb = new CallbackHandle<Void>(
+ new ResultDeserializer<Void>() {
+ @Override
+ public Void fromResult(JavaScriptObject responseObject) {
+ return null;
+ }
+ },
+ new AsyncCallback<Void>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ }
+
+ @Override
+ public void onSuccess(Void result) {
+ display(token);
+ }
+ });
+ cb.install();
+ ScriptInjector.fromString(cb.getFunctionName() + "();")
+ .setWindow(ScriptInjector.TOP_WINDOW)
+ .inject();
}
public static void refreshMenuBar() {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java
index 0bb36c852b..12e7402b90 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java
@@ -22,7 +22,6 @@ public interface GerritMessages extends Messages {
String poweredBy(String version);
String noSuchAccountMessage(String who);
-
String noSuchGroupMessage(String who);
String branchCreationFailed(String branchName, String error);
@@ -31,4 +30,6 @@ public interface GerritMessages extends Messages {
String branchCreationNotAllowedUnderRefnamePrefix(String refnamePrefix);
String branchAlreadyExists(String branchName);
String branchCreationConflict(String branchName, String existingBranchName);
+
+ String pluginFailed(String scriptPath);
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties
index 5a489d3f1a..84cf476a51 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties
@@ -3,7 +3,6 @@ windowTitle2 = {0} | {1} Code Review
poweredBy = Powered by <a href="http://code.google.com/p/gerrit/" target="_blank">Gerrit Code Review</a> ({0})
noSuchAccountMessage = {0} is not a registered user.
-
noSuchGroupMessage = Group {0} does not exist or is not visible to you.
branchCreationFailed = Creating branch {0} failed. Error: {1}
@@ -12,3 +11,5 @@ invalidRevision = The revision {0} is not valid.
branchCreationNotAllowedUnderRefnamePrefix = Branch creation is not allowed under {0}.
branchAlreadyExists = A branch with the name {0} already exists.
branchCreationConflict = Cannot create branch {0} since it conflicts with branch {1}.
+
+pluginFailed = Plugin JavaScript {0} failed to load
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
index 1a48bb5841..591d97ceb2 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
@@ -18,6 +18,8 @@ import static com.google.inject.Scopes.SINGLETON;
import static com.google.gerrit.extensions.registration.PrivateInternals_DynamicTypes.registerInParentInjectors;
import com.google.gerrit.common.data.GerritConfig;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.webui.WebUiPlugin;
import com.google.gerrit.httpd.auth.become.BecomeAnyAccountLoginServlet;
import com.google.gerrit.httpd.auth.container.HttpAuthModule;
import com.google.gerrit.httpd.auth.container.HttpsClientSslCertModule;
@@ -130,6 +132,7 @@ public class WebModule extends FactoryModule {
SINGLETON);
bind(GerritConfigProvider.class);
bind(GerritConfig.class).toProvider(GerritConfigProvider.class);
+ DynamicSet.setOf(binder(), WebUiPlugin.class);
bind(AccountManager.class);
bind(ChangeUserName.CurrentUser.class);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
index e737700088..d009261c2a 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
@@ -501,6 +501,10 @@ class HttpPluginServlet extends HttpServlet
}
if (contentType == null) {
contentType = mimeUtil.getMimeType(entry.getName(), data).toString();
+ if ("application/octet-stream".equals(contentType)
+ && entry.getName().endsWith(".js")) {
+ contentType = "application/javascript";
+ }
}
long time = entry.getTime();
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
index 28673c56ad..fdaca6016b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
@@ -16,8 +16,12 @@ package com.google.gerrit.httpd.raw;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
+import com.google.common.collect.Lists;
+import com.google.common.primitives.Bytes;
import com.google.gerrit.common.data.GerritConfig;
import com.google.gerrit.common.data.HostPageData;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.webui.WebUiPlugin;
import com.google.gerrit.httpd.HtmlDomUtil;
import com.google.gerrit.httpd.WebSession;
import com.google.gerrit.server.CurrentUser;
@@ -45,6 +49,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
@@ -65,6 +70,7 @@ public class HostPageServlet extends HttpServlet {
private final Provider<CurrentUser> currentUser;
private final Provider<WebSession> session;
private final GerritConfig config;
+ private final DynamicSet<WebUiPlugin> plugins;
private final HostPageData.Theme signedOutTheme;
private final HostPageData.Theme signedInTheme;
private final SitePaths site;
@@ -78,11 +84,13 @@ public class HostPageServlet extends HttpServlet {
HostPageServlet(final Provider<CurrentUser> cu, final Provider<WebSession> w,
final SitePaths sp, final ThemeFactory themeFactory,
final GerritConfig gc, final ServletContext servletContext,
+ final DynamicSet<WebUiPlugin> webUiPlugins,
@GerritServerConfig final Config cfg)
throws IOException, ServletException {
currentUser = cu;
session = w;
config = gc;
+ plugins = webUiPlugins;
signedOutTheme = themeFactory.getSignedOutTheme();
signedInTheme = themeFactory.getSignedInTheme();
site = sp;
@@ -163,11 +171,9 @@ public class HostPageServlet extends HttpServlet {
protected void doGet(final HttpServletRequest req,
final HttpServletResponse rsp) throws IOException {
final Page.Content page = get().get(select(req));
- final byte[] raw;
-
+ final StringWriter w = new StringWriter();
final CurrentUser user = currentUser.get();
if (user instanceof IdentifiedUser) {
- final StringWriter w = new StringWriter();
w.write(HPD_ID + ".account=");
json(((IdentifiedUser) user).getAccount(), w);
w.write(";");
@@ -183,17 +189,19 @@ public class HostPageServlet extends HttpServlet {
w.write(HPD_ID + ".theme=");
json(signedInTheme, w);
w.write(";");
-
- final byte[] userData = w.toString().getBytes("UTF-8");
- raw = concat(page.part1, userData, page.part2);
} else {
- raw = page.full;
+ w.write(HPD_ID + ".theme=");
+ json(signedOutTheme, w);
+ w.write(";");
}
+ plugins(w);
+ final byte[] hpd = w.toString().getBytes("UTF-8");
+ final byte[] raw = Bytes.concat(page.part1, hpd, page.part2);
final byte[] tosend;
if (RPCServletUtils.acceptsGzipEncoding(req)) {
rsp.setHeader("Content-Encoding", "gzip");
- tosend = raw == page.full ? page.full_gz : HtmlDomUtil.compress(raw);
+ tosend = HtmlDomUtil.compress(raw);
} else {
tosend = raw;
}
@@ -212,6 +220,20 @@ public class HostPageServlet extends HttpServlet {
}
}
+ private void plugins(StringWriter w) {
+ List<String> urls = Lists.newArrayList();
+ for (WebUiPlugin u : plugins) {
+ urls.add(String.format("plugins/%s/%s",
+ u.getPluginName(),
+ u.getJavaScriptResourcePath()));
+ }
+ if (!urls.isEmpty()) {
+ w.write(HPD_ID + ".plugins=");
+ json(urls, w);
+ w.write(";");
+ }
+ }
+
private Permutation select(final HttpServletRequest req) {
if ("0".equals(req.getParameter("s"))) {
// If s=0 is used in the URL, the user has explicitly asked us
@@ -223,20 +245,6 @@ public class HostPageServlet extends HttpServlet {
return selector.select(req);
}
- private static byte[] concat(byte[] p1, byte[] p2, byte[] p3) {
- final byte[] r = new byte[p1.length + p2.length + p3.length];
- int p = 0;
- p = append(p1, r, p);
- p = append(p2, r, p);
- p = append(p3, r, p);
- return r;
- }
-
- private static int append(byte[] src, final byte[] dst, int p) {
- System.arraycopy(src, 0, dst, p, src.length);
- return p + src.length;
- }
-
private static class FileInfo {
private final File path;
private final long time;
@@ -313,8 +321,6 @@ public class HostPageServlet extends HttpServlet {
class Content {
final byte[] part1;
final byte[] part2;
- final byte[] full;
- final byte[] full_gz;
Content(Document hostDoc) throws IOException {
final String raw = HtmlDomUtil.toString(hostDoc);
@@ -324,15 +330,6 @@ public class HostPageServlet extends HttpServlet {
}
part1 = raw.substring(0, p).getBytes("UTF-8");
part2 = raw.substring(raw.indexOf('>', p) + 1).getBytes("UTF-8");
-
- final StringWriter w = new StringWriter();
- w.write(HPD_ID + ".theme=");
- json(signedOutTheme, w);
- w.write(";");
-
- final byte[] themeData = w.toString().getBytes("UTF-8");
- full = concat(part1, themeData, part2);
- full_gz = HtmlDomUtil.compress(full);
}
}
diff --git a/gerrit-plugin-gwtui/.gitignore b/gerrit-plugin-gwtui/.gitignore
new file mode 100644
index 0000000000..2dcf1ed838
--- /dev/null
+++ b/gerrit-plugin-gwtui/.gitignore
@@ -0,0 +1,6 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
+/.settings/org.eclipse.m2e.core.prefs
+/gerrit-gwtui-plugin.iml
diff --git a/gerrit-plugin-gwtui/.settings/org.eclipse.core.resources.prefs b/gerrit-plugin-gwtui/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..839d647eef
--- /dev/null
+++ b/gerrit-plugin-gwtui/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
+encoding//src/test/java=UTF-8
+encoding/<project>=UTF-8
diff --git a/gerrit-plugin-gwtui/.settings/org.eclipse.jdt.core.prefs b/gerrit-plugin-gwtui/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..955e208d79
--- /dev/null
+++ b/gerrit-plugin-gwtui/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,295 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_on_off_tags=false
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
diff --git a/gerrit-plugin-gwtui/.settings/org.eclipse.jdt.ui.prefs b/gerrit-plugin-gwtui/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..c01882105a
--- /dev/null
+++ b/gerrit-plugin-gwtui/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+formatter_profile=_Google Format
+formatter_settings_version=12
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.keywordthis=false
+org.eclipse.jdt.ui.overrideannotation=true
diff --git a/gerrit-plugin-gwtui/pom.xml b/gerrit-plugin-gwtui/pom.xml
new file mode 100644
index 0000000000..aa6ba8f577
--- /dev/null
+++ b/gerrit-plugin-gwtui/pom.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2012 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.6-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-plugin-gwtui</artifactId>
+ <name>Gerrit Code Review - Plugin GWT UI</name>
+
+ <description>
+ API for UI plugins to build with GWT and integrate with Gerrit
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-user</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-dev</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>gwt-maven-plugin</artifactId>
+ <configuration>
+ <module>com.google.gerrit.Plugin</module>
+ <disableClassMetadata>true</disableClassMetadata>
+ <disableCastChecking>true</disableCastChecking>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>resources</goal>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project> \ No newline at end of file
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/Plugin.gwt.xml b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/Plugin.gwt.xml
new file mode 100644
index 0000000000..03edf676ca
--- /dev/null
+++ b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/Plugin.gwt.xml
@@ -0,0 +1,23 @@
+<!--
+ Copyright (C) 2012 The Android Open Source Project
+
+ 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.
+-->
+<module>
+ <define-linker name="gerrit_plugin" class="com.google.gerrit.linker.GerritPluginLinker"/>
+ <add-linker name="gerrit_plugin"/>
+ <generate-with class="com.google.gerrit.rebind.PluginGenerator">
+ <when-type-assignable class="com.google.gerrit.client.Plugin"/>
+ </generate-with>
+ <source path="client"/>
+</module>
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/client/Plugin.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/client/Plugin.java
new file mode 100644
index 0000000000..1291b79b10
--- /dev/null
+++ b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/client/Plugin.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// 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.gerrit.client;
+
+import com.google.gwt.core.client.EntryPoint;
+
+/**
+ * Base class for writing Gerrit Web UI plugins
+ *
+ * Writing a plugin:
+ * <ol>
+ * <li>Declare subtype of Plugin</li>
+ * <li>Bind WebUiPlugin to GwtPlugin implementation in Gerrit-Module</li>
+ * </ol>
+ */
+public abstract class Plugin implements EntryPoint {
+}
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/linker/GerritPluginLinker.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/linker/GerritPluginLinker.java
new file mode 100644
index 0000000000..e50334e972
--- /dev/null
+++ b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/linker/GerritPluginLinker.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// 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.gerrit.linker;
+
+import com.google.gwt.core.ext.LinkerContext;
+import com.google.gwt.core.linker.CrossSiteIframeLinker;
+
+/** Finalizes the module manifest file with the selection script. */
+public final class GerritPluginLinker extends CrossSiteIframeLinker {
+ @Override
+ public String getDescription() {
+ return "Gerrit GWT UI plugin";
+ }
+
+ @Override
+ protected String getJsComputeUrlForResource(LinkerContext context) {
+ return "com/google/gerrit/linker/computeUrlForPluginResource.js";
+ }
+}
diff --git a/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/rebind/PluginGenerator.java b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/rebind/PluginGenerator.java
new file mode 100644
index 0000000000..71666f91e6
--- /dev/null
+++ b/gerrit-plugin-gwtui/src/main/java/com/google/gerrit/rebind/PluginGenerator.java
@@ -0,0 +1,95 @@
+// Copyright (C) 2012 The Android Open Source Project
+// Copyright 2008 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.gerrit.rebind;
+
+import java.io.PrintWriter;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
+import com.google.gwt.user.rebind.SourceWriter;
+
+/**
+ * Write the top layer in the Gadget bootstrap sandwich and generate a stub
+ * manifest that will be completed by the linker.
+ *
+ * Based on gwt-gadgets GadgetGenerator class
+ */
+public class PluginGenerator extends Generator {
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext context,
+ String typeName) throws UnableToCompleteException {
+
+ // The TypeOracle knows about all types in the type system
+ TypeOracle typeOracle = context.getTypeOracle();
+
+ // Get a reference to the type that the generator should implement
+ JClassType sourceType = typeOracle.findType(typeName);
+
+ // Ensure that the requested type exists
+ if (sourceType == null) {
+ logger.log(TreeLogger.ERROR, "Could not find requested typeName", null);
+ throw new UnableToCompleteException();
+ }
+
+ // Make sure the Gadget type is correctly defined
+ validateType(logger, sourceType);
+
+ // Pick a name for the generated class to not conflict.
+ String generatedSimpleSourceName = sourceType.getSimpleSourceName()
+ + "PluginImpl";
+
+ // Begin writing the generated source.
+ ClassSourceFileComposerFactory f = new ClassSourceFileComposerFactory(
+ sourceType.getPackage().getName(), generatedSimpleSourceName);
+ f.addImport(GWT.class.getName());
+ f.setSuperclass(typeName);
+
+ // All source gets written through this Writer
+ PrintWriter out = context.tryCreate(logger,
+ sourceType.getPackage().getName(), generatedSimpleSourceName);
+
+ // If an implementation already exists, we don't need to do any work
+ if (out != null) {
+
+ // We really use a SourceWriter since it's convenient
+ SourceWriter sw = f.createSourceWriter(context, out);
+ sw.println("public " + generatedSimpleSourceName + "() {");
+ sw.indent();
+ sw.println("onModuleLoad();");
+ sw.outdent();
+ sw.println("}");
+
+ sw.commit(logger);
+ }
+
+ return f.getCreatedClassName();
+ }
+
+ protected void validateType(TreeLogger logger, JClassType type)
+ throws UnableToCompleteException {
+ if (!type.isDefaultInstantiable()) {
+ logger.log(TreeLogger.ERROR, "Plugin types must be default instantiable",
+ null);
+ throw new UnableToCompleteException();
+ }
+ }
+}
diff --git a/gerrit-plugin-gwtui/src/main/resources/com/google/gerrit/linker/computeUrlForPluginResource.js b/gerrit-plugin-gwtui/src/main/resources/com/google/gerrit/linker/computeUrlForPluginResource.js
new file mode 100644
index 0000000000..3e20e94c6e
--- /dev/null
+++ b/gerrit-plugin-gwtui/src/main/resources/com/google/gerrit/linker/computeUrlForPluginResource.js
@@ -0,0 +1,3 @@
+function computeUrlForResource(resource) {
+ return __MODULE_FUNC__.__moduleBase + resource;
+}
diff --git a/pom.xml b/pom.xml
index c1e2fd8b18..af02f9fb7f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -98,7 +98,8 @@ limitations under the License.
<modules>
<module>gerrit-plugin-api</module>
<module>gerrit-plugin-archetype</module>
- </modules>
+ <module>gerrit-plugin-gwtui</module>
+ </modules>
</profile>
<profile>
<activation>