Merge branch 'stable-2.16'

* stable-2.16:
  Remove mention that H2 is not recommended for large installations
  Temp fix for projects list ordering in PolyGerrit
  EditInfo: Include the change edit ref
  Add missing documentation of base_patch_set_number in edit API
  Add postgresql/postgres/pgsql to mime list
  Build all languages for highlight.js
  Update git submodules
  Fix "stream has already been operated upon or closed"
  Merge top menu items contributed by plugins

Change-Id: I21aa77a4a068e16089709ee44a1fd2cf3d5b2c58
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index b60e56d..ccec37b1 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -2593,7 +2593,9 @@
        "subject":"Use an EventBus to manage star icons",
        "message":"Use an EventBus to manage star icons\n\nImage widgets that need to ..."
     },
-    "base_revision":"c35558e0925e6985c91f3a16921537d5e572b7a3"
+    "base_patch_set_number":1,
+    "base_revision":"c35558e0925e6985c91f3a16921537d5e572b7a3",
+    "ref":"refs/users/01/1000001/edit-76482/1"
   }
 ----
 
@@ -4825,7 +4827,9 @@
          "subject":"Implement feature X",
          "message":"Implement feature X\n\nWith this feature ..."
       },
+      "base_patch_set_number":1,
       "base_revision":"674ac754f91e64a0efb8087e59a176484bd534d1"
+      "ref":"refs/users/01/1000001/edit-42622/1"
     }
 ----
 
@@ -6340,15 +6344,17 @@
 
 [options="header",cols="1,^1,5"]
 |===========================
-|Field Name     ||Description
-|`commit`       ||The commit of change edit as
+|Field Name             ||Description
+|`commit`               ||The commit of change edit as
 link:#commit-info[CommitInfo] entity.
-|`base_revision`||The revision of the patch set the change edit is based on.
-|`fetch`        |optional|
+|`base_patch_set_number`||The patch set number of the patch set the change edit is based on.
+|`base_revision`        ||The revision of the patch set the change edit is based on.
+|`ref`                  ||The ref of the change edit.
+|`fetch`                |optional|
 Information about how to fetch this patch set. The fetch information is
 provided as a map that maps the protocol name ("`git`", "`http`",
 "`ssh`") to link:#fetch-info[FetchInfo] entities.
-|`files`        |optional|
+|`files`                |optional|
 The files of the change edit as a map that maps the file names to
 link:#file-info[FileInfo] entities.
 |===========================
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index 6f4c98a..dd28d20 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -364,7 +364,6 @@
     daemon.setAuditEventModuleForTesting(new FakeGroupAuditService.Module());
     daemon.setAdditionalSysModuleForTesting(testSysModule);
     daemon.setEnableSshd(desc.useSsh());
-    daemon.setSlave(isSlave(baseConfig));
 
     if (desc.memory()) {
       checkArgument(additionalArgs.length == 0, "cannot pass args to in-memory server");
@@ -381,6 +380,7 @@
       @Nullable InMemoryRepositoryManager inMemoryRepoManager)
       throws Exception {
     Config cfg = desc.buildConfig(baseConfig);
+    daemon.setSlave(isSlave(baseConfig) || cfg.getBoolean("container", "slave", false));
     mergeTestConfig(cfg);
     // Set the log4j configuration to an invalid one to prevent system logs
     // from getting configured and creating log files.
diff --git a/java/com/google/gerrit/extensions/common/EditInfo.java b/java/com/google/gerrit/extensions/common/EditInfo.java
index 46ef879..0cd5af3 100644
--- a/java/com/google/gerrit/extensions/common/EditInfo.java
+++ b/java/com/google/gerrit/extensions/common/EditInfo.java
@@ -20,6 +20,7 @@
   public CommitInfo commit;
   public int basePatchSetNumber;
   public String baseRevision;
+  public String ref;
   public Map<String, FetchInfo> fetch;
   public Map<String, FileInfo> files;
 }
diff --git a/java/com/google/gerrit/server/edit/ChangeEditJson.java b/java/com/google/gerrit/server/edit/ChangeEditJson.java
index bf20404..55e0aef 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditJson.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditJson.java
@@ -53,6 +53,7 @@
     out.commit = fillCommit(edit.getEditCommit());
     out.baseRevision = edit.getBasePatchSet().getRevision().get();
     out.basePatchSetNumber = edit.getBasePatchSet().getPatchSetId();
+    out.ref = edit.getRefName();
     if (downloadCommands) {
       out.fetch = fillFetchMap(edit);
     }
diff --git a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
index 662ba37..879fddd 100644
--- a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
+++ b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
@@ -22,6 +22,7 @@
 import static java.util.Objects.requireNonNull;
 import static java.util.stream.Collectors.toMap;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 import com.google.common.flogger.FluentLogger;
@@ -58,7 +59,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.stream.Stream;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.Ref;
@@ -365,17 +365,17 @@
   private Map<Change.Id, Branch.NameKey> visibleChangesByScan(Repository repo)
       throws PermissionBackendException {
     Project.NameKey p = projectState.getNameKey();
-    Stream<ChangeNotesResult> s;
+    ImmutableList<ChangeNotesResult> changes;
     try {
-      s = changeNotesFactory.scan(repo, p);
+      changes = changeNotesFactory.scan(repo, p).collect(toImmutableList());
     } catch (IOException e) {
       logger.atSevere().withCause(e).log(
           "Cannot load changes for project %s, assuming no changes are visible", p);
       return Collections.emptyMap();
     }
 
-    Map<Change.Id, Branch.NameKey> result = Maps.newHashMapWithExpectedSize((int) s.count());
-    for (ChangeNotesResult notesResult : s.collect(toImmutableList())) {
+    Map<Change.Id, Branch.NameKey> result = Maps.newHashMapWithExpectedSize(changes.size());
+    for (ChangeNotesResult notesResult : changes) {
       ChangeNotes notes = toNotes(notesResult);
       if (notes != null) {
         result.put(notes.getChangeId(), notes.getChange().getDest());
diff --git a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
index e06b406..e653bc4 100644
--- a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
@@ -17,6 +17,7 @@
 import com.google.common.collect.ListMultimap;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.api.GerritApi;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -26,6 +27,7 @@
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.RestCollection;
+import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
@@ -42,7 +44,9 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.io.IOException;
+import java.util.List;
 import org.eclipse.jgit.lib.Constants;
+import org.kohsuke.args4j.Option;
 
 @Singleton
 public class ProjectsCollection
@@ -51,23 +55,27 @@
 
   private final DynamicMap<RestView<ProjectResource>> views;
   private final Provider<ListProjects> list;
+  private final Provider<GerritApi> gApi;
   private final Provider<QueryProjects> queryProjects;
   private final ProjectCache projectCache;
   private final PermissionBackend permissionBackend;
   private final Provider<CurrentUser> user;
 
   private boolean hasQuery;
+  private boolean isDisplayActiveAndReadonlyQuery;
 
   @Inject
   public ProjectsCollection(
       DynamicMap<RestView<ProjectResource>> views,
       Provider<ListProjects> list,
+      Provider<GerritApi> gApi,
       Provider<QueryProjects> queryProjects,
       ProjectCache projectCache,
       PermissionBackend permissionBackend,
       Provider<CurrentUser> user) {
     this.views = views;
     this.list = list;
+    this.gApi = gApi;
     this.queryProjects = queryProjects;
     this.projectCache = projectCache;
     this.permissionBackend = permissionBackend;
@@ -78,11 +86,62 @@
   public void setParams(ListMultimap<String, String> params) throws BadRequestException {
     // The --query option is defined in QueryProjects
     this.hasQuery = params.containsKey("query");
+    List<String> queryParams = params.get("query");
+    this.isDisplayActiveAndReadonlyQuery =
+        queryParams.size() == 1
+            && queryParams.get(0).toLowerCase().trim().equals("state:active or state:read-only");
   }
 
   @Override
   public RestView<TopLevelResource> list() {
     if (hasQuery) {
+      // Temporary workaround for v2.16. Should be removed as soon as the Projects Lucene Index
+      // sorting is fixed
+      if (isDisplayActiveAndReadonlyQuery) {
+        return new RestReadView<TopLevelResource>() {
+          private int limit;
+          private int start;
+
+          @Option(
+              name = "--limit",
+              aliases = {"-n"},
+              metaVar = "CNT",
+              usage = "maximum number of projects to list")
+          public void setLimit(int limit) {
+            this.limit = limit;
+          }
+
+          @Option(
+              name = "--start",
+              aliases = {"-S"},
+              metaVar = "CNT",
+              usage = "number of projects to skip")
+          public void setStart(int start) {
+            this.start = start;
+          }
+
+          @SuppressWarnings("unused")
+          @Option(
+              name = "--query",
+              aliases = {"-q"},
+              usage = "project query")
+          public void setQuery(String query) {
+            // Explicitly ignored because this fix is for full listing only
+          }
+
+          @Override
+          public Object apply(TopLevelResource resource)
+              throws AuthException, BadRequestException, ResourceConflictException, Exception {
+            return gApi.get()
+                .projects()
+                .list()
+                .withDescription(true)
+                .withStart(start)
+                .withLimit(limit)
+                .get();
+          }
+        };
+      }
       return queryProjects.get();
     }
     return list.get().setFormat(OutputFormat.JSON);
diff --git a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
index 1a0f256..f6a7544 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
@@ -338,7 +338,17 @@
   }
 
   @Test
-  public void uploadPackNoSearchingChangeCacheImpl() throws Exception {
+  public void uploadPackNoSearchingChangeCacheImplMaster() throws Exception {
+    uploadPackNoSearchingChangeCacheImpl();
+  }
+
+  @Test
+  @GerritConfig(name = "container.slave", value = "true")
+  public void uploadPackNoSearchingChangeCacheImplSlave() throws Exception {
+    uploadPackNoSearchingChangeCacheImpl();
+  }
+
+  private void uploadPackNoSearchingChangeCacheImpl() throws Exception {
     allow("refs/heads/*", Permission.READ, REGISTERED_USERS);
 
     setApiUser(user);
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
index 738d29e..a77c66d 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
@@ -179,11 +179,16 @@
     },
 
     _computeLinks(defaultLinks, userLinks, adminLinks, topMenus, docBaseUrl) {
-      const links = defaultLinks.slice();
+      const links = defaultLinks.map(menu => {
+        return {
+          title: menu.title,
+          links: menu.links.slice(),
+        };
+      });
       if (userLinks && userLinks.length > 0) {
         links.push({
           title: 'Your',
-          links: userLinks,
+          links: userLinks.slice(),
         });
       }
       const docLinks = this._getDocLinks(docBaseUrl, DOCUMENTATION_LINKS);
@@ -196,13 +201,20 @@
       }
       links.push({
         title: 'Browse',
-        links: adminLinks,
+        links: adminLinks.slice(),
       });
+      const topMenuLinks = [];
+      links.forEach(link => { topMenuLinks[link.title] = link.links; });
       for (const m of topMenus) {
-        links.push({
-          title: m.name,
-          links: m.items.map(this._fixCustomMenuItem),
-        });
+        const items = m.items.map(this._fixCustomMenuItem);
+        if (m.name in topMenuLinks) {
+          items.forEach(link => { topMenuLinks[m.name].push(link); });
+        } else {
+          links.push({
+            title: m.name,
+            links: topMenuLinks[m.name] = items,
+          });
+        }
       }
       return links;
     },
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
index b6e64ec..582ca61 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
@@ -191,6 +191,121 @@
       }]);
     });
 
+    test('merge top menus', () => {
+      const adminLinks = [{
+        name: 'Repos',
+        url: '/repos',
+      }];
+      const topMenus = [{
+        name: 'Plugins',
+        items: [{
+          name: 'Manage',
+          target: '_blank',
+          url: 'https://gerrit/plugins/plugin-manager/static/index.html',
+        }],
+      }, {
+        name: 'Plugins',
+        items: [{
+          name: 'Create',
+          target: '_blank',
+          url: 'https://gerrit/plugins/plugin-manager/static/create.html',
+        }],
+      }];
+      assert.deepEqual(element._computeLinks([], [], adminLinks, topMenus), [{
+        title: 'Browse',
+        links: adminLinks,
+      }, {
+        title: 'Plugins',
+        links: [{
+          name: 'Manage',
+          external: true,
+          url: 'https://gerrit/plugins/plugin-manager/static/index.html',
+        }, {
+          name: 'Create',
+          external: true,
+          url: 'https://gerrit/plugins/plugin-manager/static/create.html',
+        }],
+      }]);
+    });
+
+    test('merge top menus in default links', () => {
+      const defaultLinks = [{
+        title: 'Faves',
+        links: [{
+          name: 'Pinterest',
+          url: 'https://pinterest.com',
+        }],
+      }];
+      const topMenus = [{
+        name: 'Faves',
+        items: [{
+          name: 'Manage',
+          target: '_blank',
+          url: 'https://gerrit/plugins/plugin-manager/static/index.html',
+        }],
+      }];
+      assert.deepEqual(element._computeLinks(defaultLinks, [], [], topMenus), [{
+        title: 'Faves',
+        links: defaultLinks[0].links.concat([{
+          name: 'Manage',
+          external: true,
+          url: 'https://gerrit/plugins/plugin-manager/static/index.html',
+        }]),
+      }, {
+        title: 'Browse',
+        links: [],
+      }]);
+    });
+
+    test('merge top menus in user links', () => {
+      const userLinks = [{
+        name: 'Facebook',
+        url: 'https://facebook.com',
+      }];
+      const topMenus = [{
+        name: 'Your',
+        items: [{
+          name: 'Manage',
+          target: '_blank',
+          url: 'https://gerrit/plugins/plugin-manager/static/index.html',
+        }],
+      }];
+      assert.deepEqual(element._computeLinks([], userLinks, [], topMenus), [{
+        title: 'Your',
+        links: userLinks.concat([{
+          name: 'Manage',
+          external: true,
+          url: 'https://gerrit/plugins/plugin-manager/static/index.html',
+        }]),
+      }, {
+        title: 'Browse',
+        links: [],
+      }]);
+    });
+
+    test('merge top menus in admin links', () => {
+      const adminLinks = [{
+        name: 'Repos',
+        url: '/repos',
+      }];
+      const topMenus = [{
+        name: 'Browse',
+        items: [{
+          name: 'Manage',
+          target: '_blank',
+          url: 'https://gerrit/plugins/plugin-manager/static/index.html',
+        }],
+      }];
+      assert.deepEqual(element._computeLinks([], [], adminLinks, topMenus), [{
+        title: 'Browse',
+        links: adminLinks.concat([{
+          name: 'Manage',
+          external: true,
+          url: 'https://gerrit/plugins/plugin-manager/static/index.html',
+        }]),
+      }]);
+    });
+
     test('register URL', () => {
       const config = {
         auth: {
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
index 30ac95d..f8d9e11 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
@@ -63,6 +63,7 @@
     'text/x-objectivec': 'objectivec',
     'text/x-ocaml': 'ocaml',
     'text/x-perl': 'perl',
+    'text/x-pgsql': 'pgsql', // postgresql
     'text/x-php': 'php',
     'text/x-protobuf': 'protobuf',
     'text/x-puppet': 'puppet',
diff --git a/resources/com/google/gerrit/server/mime/mime-types.properties b/resources/com/google/gerrit/server/mime/mime-types.properties
index 5f5979d..77d6f0f 100644
--- a/resources/com/google/gerrit/server/mime/mime-types.properties
+++ b/resources/com/google/gerrit/server/mime/mime-types.properties
@@ -149,6 +149,7 @@
 patch = text/x-diff
 pcss = text/x-pcss
 pgp = application/pgp
+pgsql = text/x-pgsql
 php = text/x-php
 php3 = text/x-php
 php4 = text/x-php
@@ -159,6 +160,8 @@
 pl = text/x-perl
 pls = text/x-plsql
 pm = text/x-perl
+postgres = text/x-pgsql
+postgresql = text/x-pgsql
 pp = text/x-puppet
 pro = text/x-idl
 properties = text/x-ini