Merge "ChangeEditModifier: Reject invalid file paths as '400 Bad Request'"
diff --git a/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java b/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
index 2b068aa..8f7e360 100644
--- a/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
+++ b/java/com/google/gerrit/server/cache/h2/H2CacheFactory.java
@@ -47,6 +47,10 @@
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config;
+/**
+ * Creates persistent caches depending on gerrit.config parameters. If the cache.directory property
+ * is unset, it will fall back to in-memory caches.
+ */
@Singleton
class H2CacheFactory implements PersistentCacheFactory, LifecycleListener {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
diff --git a/java/com/google/gerrit/server/change/FileInfoJson.java b/java/com/google/gerrit/server/change/FileInfoJson.java
index a823975..aca4fb0 100644
--- a/java/com/google/gerrit/server/change/FileInfoJson.java
+++ b/java/com/google/gerrit/server/change/FileInfoJson.java
@@ -21,6 +21,7 @@
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
import com.google.gerrit.extensions.common.FileInfo;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListEntry;
@@ -30,6 +31,8 @@
import com.google.inject.Singleton;
import java.util.Map;
import java.util.TreeMap;
+import java.util.concurrent.ExecutionException;
+import org.eclipse.jgit.errors.NoMergeBaseException;
import org.eclipse.jgit.lib.ObjectId;
@Singleton
@@ -42,31 +45,44 @@
}
public Map<String, FileInfo> toFileInfoMap(Change change, PatchSet patchSet)
- throws PatchListNotAvailableException {
+ throws ResourceConflictException, PatchListNotAvailableException {
return toFileInfoMap(change, patchSet.commitId(), null);
}
public Map<String, FileInfo> toFileInfoMap(
Change change, ObjectId objectId, @Nullable PatchSet base)
- throws PatchListNotAvailableException {
+ throws ResourceConflictException, PatchListNotAvailableException {
ObjectId a = base != null ? base.commitId() : null;
return toFileInfoMap(change, PatchListKey.againstCommit(a, objectId, Whitespace.IGNORE_NONE));
}
public Map<String, FileInfo> toFileInfoMap(Change change, ObjectId objectId, int parent)
- throws PatchListNotAvailableException {
+ throws ResourceConflictException, PatchListNotAvailableException {
return toFileInfoMap(
change, PatchListKey.againstParentNum(parent + 1, objectId, Whitespace.IGNORE_NONE));
}
private Map<String, FileInfo> toFileInfoMap(Change change, PatchListKey key)
- throws PatchListNotAvailableException {
+ throws ResourceConflictException, PatchListNotAvailableException {
return toFileInfoMap(change.getProject(), key);
}
public Map<String, FileInfo> toFileInfoMap(Project.NameKey project, PatchListKey key)
- throws PatchListNotAvailableException {
- PatchList list = patchListCache.get(key, project);
+ throws ResourceConflictException, PatchListNotAvailableException {
+ PatchList list;
+ try {
+ list = patchListCache.get(key, project);
+ } catch (PatchListNotAvailableException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof ExecutionException) {
+ cause = cause.getCause();
+ }
+ if (cause instanceof NoMergeBaseException) {
+ throw new ResourceConflictException(
+ String.format("Cannot create auto merge commit: %s", e.getMessage()), e);
+ }
+ throw e;
+ }
Map<String, FileInfo> files = new TreeMap<>();
for (PatchListEntry e : list.getPatches()) {
diff --git a/java/com/google/gerrit/server/change/RevisionJson.java b/java/com/google/gerrit/server/change/RevisionJson.java
index fbd14c4..a4994fd 100644
--- a/java/com/google/gerrit/server/change/RevisionJson.java
+++ b/java/com/google/gerrit/server/change/RevisionJson.java
@@ -49,6 +49,7 @@
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.Extension;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GpgException;
@@ -311,9 +312,13 @@
}
if (has(ALL_FILES) || (out.isCurrent && has(CURRENT_FILES))) {
- out.files = fileInfoJson.toFileInfoMap(c, in);
- out.files.remove(Patch.COMMIT_MSG);
- out.files.remove(Patch.MERGE_LIST);
+ try {
+ out.files = fileInfoJson.toFileInfoMap(c, in);
+ out.files.remove(Patch.COMMIT_MSG);
+ out.files.remove(Patch.MERGE_LIST);
+ } catch (ResourceConflictException e) {
+ logger.atWarning().withCause(e).log("creating file list failed");
+ }
}
if (out.isCurrent && has(CURRENT_ACTIONS) && userProvider.get().isIdentifiedUser()) {
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
index aa82247..cbc1b79 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
@@ -185,7 +185,8 @@
@Override
public Response<EditInfo> apply(ChangeResource rsrc)
- throws AuthException, IOException, ResourceNotFoundException, PermissionBackendException {
+ throws AuthException, IOException, ResourceNotFoundException, ResourceConflictException,
+ PermissionBackendException {
Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser());
if (!edit.isPresent()) {
return Response.none();
diff --git a/java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java b/java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java
index 0ee8279..0d5ab88 100644
--- a/java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/FilesInCommitCollection.java
@@ -20,6 +20,7 @@
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
@@ -86,7 +87,7 @@
@Override
public Response<Map<String, FileInfo>> apply(CommitResource resource)
- throws PatchListNotAvailableException {
+ throws ResourceConflictException, PatchListNotAvailableException {
RevCommit commit = resource.getCommit();
PatchListKey key;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
index 359a039..e46f959 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
@@ -192,15 +192,20 @@
}
}
- /**
- * For Polymer 2, use shadowRoot.getSelection instead.
- */
_getSelection() {
- const diffHost = util.querySelector(document.body, 'gr-diff');
- const selection = diffHost &&
- diffHost.shadowRoot &&
- diffHost.shadowRoot.getSelection();
- return selection ? selection: window.getSelection();
+ const diffHosts = util.querySelectorAll(document.body, 'gr-diff');
+ if (!diffHosts.length) return window.getSelection();
+
+ const curDiffHost = diffHosts.find(diffHost => {
+ if (!diffHost || !diffHost.shadowRoot) return false;
+ const selection = diffHost.shadowRoot.getSelection();
+ // Pick the one with valid selection:
+ // https://developer.mozilla.org/en-US/docs/Web/API/Selection/type
+ return selection && selection.type !== 'None';
+ });
+
+ return curDiffHost ?
+ curDiffHost.shadowRoot.getSelection(): window.getSelection();
}
/**
diff --git a/polygerrit-ui/app/scripts/util.js b/polygerrit-ui/app/scripts/util.js
index 672c43f..565b9b3 100644
--- a/polygerrit-ui/app/scripts/util.js
+++ b/polygerrit-ui/app/scripts/util.js
@@ -105,7 +105,7 @@
*/
util.querySelector = (el, selector) => {
let nodes = [el];
- let element = null;
+ let result = null;
while (nodes.length) {
const node = nodes.pop();
@@ -113,19 +113,49 @@
if (!node || !node.querySelector) continue;
// Try find it with native querySelector directly
- element = node.querySelector(selector);
+ result = node.querySelector(selector);
- if (element) {
+ if (result) {
break;
- } else if (node.shadowRoot) {
- // If shadowHost detected, add the host and its children
- nodes = nodes.concat(Array.from(node.children));
- nodes.push(node.shadowRoot);
- } else {
- nodes = nodes.concat(Array.from(node.children));
}
+
+ // Add all nodes with shadowRoot and loop through
+ const allShadowNodes = [...node.querySelectorAll('*')]
+ .filter(child => !!child.shadowRoot)
+ .map(child => child.shadowRoot);
+ nodes = nodes.concat(allShadowNodes);
}
- return element;
+ return result;
+ };
+
+ /**
+ * Query selector all dom elements matching with certain selector.
+ *
+ * This is shadow DOM compatible, but only works when selector is within
+ * one shadow host, won't work if your selector is crossing
+ * multiple shadow hosts.
+ *
+ * Note: this can be very expensive, only use when have to.
+ */
+ util.querySelectorAll = (el, selector) => {
+ let nodes = [el];
+ const results = new Set();
+ while (nodes.length) {
+ const node = nodes.pop();
+
+ if (!node || !node.querySelectorAll) continue;
+
+ // Try find all from regular children
+ [...node.querySelectorAll(selector)]
+ .forEach(el => results.add(el));
+
+ // Add all nodes with shadowRoot and loop through
+ const allShadowNodes = [...node.querySelectorAll('*')]
+ .filter(child => !!child.shadowRoot)
+ .map(child => child.shadowRoot);
+ nodes = nodes.concat(allShadowNodes);
+ }
+ return [...results];
};
window.util = util;