Merge "SubmitRuleEvaluator: Simply logging of errors"
diff --git a/Documentation/dev-design-doc-conclusion-template.md b/Documentation/dev-design-doc-conclusion-template.md
index 36bfa2a..0625f2b 100644
--- a/Documentation/dev-design-doc-conclusion-template.md
+++ b/Documentation/dev-design-doc-conclusion-template.md
@@ -1,3 +1,13 @@
+---
+title: "Design Doc - ${title} - Conclusion"
+sidebar: gerritdoc_sidebar
+permalink: design-doc-${folder-name}-conclusion.html
+hide_sidebar: true
+hide_navtoggle: true
+toc: false
+folder: design-docs/${folder-name}
+---
+
# Conclusion
Describe which decision was made and what were the reasons for it.
diff --git a/Documentation/dev-design-doc-index-template.md b/Documentation/dev-design-doc-index-template.md
index 604126d..10b4a81 100644
--- a/Documentation/dev-design-doc-index-template.md
+++ b/Documentation/dev-design-doc-index-template.md
@@ -1,3 +1,13 @@
+---
+title: "Design Doc - ${title}"
+sidebar: gerritdoc_sidebar
+permalink: design-doc-${folder-name}.html
+hide_sidebar: true
+hide_navtoggle: true
+toc: false
+folder: design-docs/${folder-name}
+---
+
# Design Doc - ${title}
* [Use Cases](use-cases.html)
diff --git a/Documentation/dev-design-doc-solution-template.md b/Documentation/dev-design-doc-solution-template.md
index 66b8ae7..8935902 100644
--- a/Documentation/dev-design-doc-solution-template.md
+++ b/Documentation/dev-design-doc-solution-template.md
@@ -1,3 +1,13 @@
+---
+title: "Design Doc - ${title} - Solution - ${solution-name}"
+sidebar: gerritdoc_sidebar
+permalink: design-doc-${folder-name}-solution-${solution-name}.html
+hide_sidebar: true
+hide_navtoggle: true
+toc: false
+folder: design-docs/${folder-name}
+---
+
# Solution - ${solution-name}
## <a id="overview"> Overview
@@ -20,8 +30,13 @@
### <a id="scalability"> Scalability
-How does the solution scale? Consider both, data size increase (if
-applicable) and traffic increase (if applicable).
+How does the solution scale?
+
+If applicable, consider:
+
+* data size increase
+* traffic increase
+* effects on replication across sites (master-slave and master-master)
## <a id="alternatives-considered"> Alternatives Considered
diff --git a/Documentation/dev-design-doc-use-cases-template.md b/Documentation/dev-design-doc-use-cases-template.md
index 704ad14c..02c2fb5 100644
--- a/Documentation/dev-design-doc-use-cases-template.md
+++ b/Documentation/dev-design-doc-use-cases-template.md
@@ -1,3 +1,13 @@
+---
+title: "Design Doc - ${title} - Use Cases"
+sidebar: gerritdoc_sidebar
+permalink: design-doc-${folder-name}-use-cases.html
+hide_sidebar: true
+hide_navtoggle: true
+toc: false
+folder: design-docs/${folder-name}
+---
+
# Use Cases
In a few sentences, describe the use-cases as interactions between a
diff --git a/WORKSPACE b/WORKSPACE
index 350dba1..6a86c19 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1251,8 +1251,8 @@
bower_archive(
name = "page",
package = "visionmedia/page.js",
- sha1 = "51a05428dd4f68fae1df5f12d0e2b61ba67f7757",
- version = "1.7.1",
+ sha1 = "4a31889cd75cc5e7f68a4c7f256eecaf27102eee",
+ version = "1.11.4",
)
bower_archive(
diff --git a/contrib/refresh_plugin_in_testsite.sh b/contrib/refresh_plugin_in_testsite.sh
new file mode 100755
index 0000000..bb42ce8
--- /dev/null
+++ b/contrib/refresh_plugin_in_testsite.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+# This script compiles a Gerrit plugin whose name is passed as first parameter
+# and copies it over to the plugin folder of the testsite. The path to the
+# testsite needs to be provided by the variable GERRIT_TESTSITE or as second
+# parameter.
+
+SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
+GERRIT_CODE_DIR="$SCRIPT_DIR/.."
+cd "$GERRIT_CODE_DIR"
+
+if [ "$#" -lt 1 ]
+then
+ echo "No plugin name provided as first argument. Stopping."
+ exit 1
+else
+ PLUGIN_NAME="$1"
+fi
+
+
+if [ "$#" -lt 2 ]
+then
+ if [ -z ${GERRIT_TESTSITE+x} ]
+ then
+ echo "Path to local testsite is neiter set as GERRIT_TESTSITE nor passed as second argument. Stopping."
+ exit 1
+ fi
+else
+ GERRIT_TESTSITE="$2"
+fi
+
+if [ ! -d "$GERRIT_TESTSITE" ]
+then
+ echo "Testsite directory $GERRIT_TESTSITE does not exist. Stopping."
+ exit 1
+fi
+
+bazel build //plugins/"$PLUGIN_NAME"/...
+if [ $? -ne 0 ]
+then
+ echo "Building the $PLUGIN_NAME plugin failed"
+ exit 1
+fi
+
+yes | cp -f "$GERRIT_CODE_DIR/bazel-genfiles/plugins/$PLUGIN_NAME/$PLUGIN_NAME.jar" "$GERRIT_TESTSITE/plugins/"
+if [ $? -eq 0 ]
+then
+ echo "Plugin $PLUGIN_NAME copied successfully to testsite."
+fi
diff --git a/contrib/show_new_gerrit_doc_in_chrome.sh b/contrib/show_new_gerrit_doc_in_chrome.sh
new file mode 100755
index 0000000..d57bc8a
--- /dev/null
+++ b/contrib/show_new_gerrit_doc_in_chrome.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+# This script builds Gerrit's documentation and shows the current state in
+# Chrome. Specific pages (e.g. rest-api-changes.txt) including anchors can be
+# passed as parameter to jump directly to them.
+
+SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
+GERRIT_CODE_DIR="$SCRIPT_DIR/.."
+cd "$GERRIT_CODE_DIR"
+
+bazel build Documentation:searchfree
+if [ $? -ne 0 ]
+then
+ echo "Building the documentation failed. Stopping."
+ exit 1
+fi
+
+TMP_DOCS_DIR=/tmp/gerrit_docs
+rm -rf "$TMP_DOCS_DIR"
+unzip bazel-bin/Documentation/searchfree.zip -d "$TMP_DOCS_DIR" </dev/null >/dev/null 2>&1 & disown
+if [ $? -ne 0 ]
+then
+ echo "Unzipping the documentation to $TMP_DOCS_DIR failed. Stopping."
+ exit 1
+fi
+
+if [ "$#" -lt 1 ]
+then
+ FILE_NAME="index.html"
+else
+ FILE_NAME="$1"
+fi
+DOC_FILE_NAME="${FILE_NAME/.txt/.html}"
+google-chrome "file:///$TMP_DOCS_DIR/Documentation/$DOC_FILE_NAME" </dev/null >/dev/null 2>&1 & disown
diff --git a/contrib/start_testsite.sh b/contrib/start_testsite.sh
new file mode 100755
index 0000000..014eba9
--- /dev/null
+++ b/contrib/start_testsite.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+# This script starts the local testsite in debug mode. If the flag "-u" is
+# passed, Gerrit is built from the current state of the repository and the
+# testsite is refreshed. The path to the testsite needs to be provided by
+# the variable GERRIT_TESTSITE or as parameter (after any used flags).
+# The testsite can be stopped by interrupting this script.
+
+SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
+GERRIT_CODE_DIR="$SCRIPT_DIR/.."
+cd "$GERRIT_CODE_DIR"
+
+UPDATE=false
+while getopts ':u' flag; do
+ case "${flag}" in
+ u) UPDATE=true ;;
+ esac
+done
+shift $(($OPTIND-1))
+
+if [ "$#" -lt 1 ]
+then
+ if [ -z ${GERRIT_TESTSITE+x} ]
+ then
+ echo "Path to local testsite is neither set as GERRIT_TESTSITE nor passed as first argument. Stopping."
+ exit 1
+ fi
+else
+ GERRIT_TESTSITE="$1"
+fi
+
+if [ "$UPDATE" = true ]
+then
+ echo "Refreshing testsite"
+ bazel build gerrit
+ if [ $? -ne 0 ]
+ then
+ echo "Build failed. Stopping."
+ exit 1
+ fi
+ $(bazel info output_base)/external/local_jdk/bin/java -jar bazel-bin/gerrit.war init --batch -d "$GERRIT_TESTSITE"
+ if [ $? -ne 0 ]
+ then
+ echo "Patching the testsite failed. Stopping."
+ exit 1
+ fi
+fi
+
+$(bazel info output_base)/external/local_jdk/bin/java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 bazel-bin/gerrit.war daemon -d "$GERRIT_TESTSITE" --console-log
diff --git a/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java b/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
index e3ab70d..aa38c27 100644
--- a/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
+++ b/java/com/google/gerrit/httpd/ProjectBasicAuthFilter.java
@@ -31,6 +31,7 @@
import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.account.AuthResult;
import com.google.gerrit.server.account.AuthenticationFailedException;
+import com.google.gerrit.server.account.externalids.PasswordVerifier;
import com.google.gerrit.server.auth.NoSuchUserException;
import com.google.gerrit.server.config.AuthConfig;
import com.google.inject.Inject;
@@ -140,7 +141,7 @@
GitBasicAuthPolicy gitBasicAuthPolicy = authConfig.getGitBasicAuthPolicy();
if (gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP
|| gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP_LDAP) {
- if (who.checkPassword(password, username)) {
+ if (PasswordVerifier.checkPassword(who.getExternalIds(), username, password)) {
return succeedAuthentication(who);
}
}
@@ -157,7 +158,7 @@
setUserIdentified(whoAuthResult.getAccountId());
return true;
} catch (NoSuchUserException e) {
- if (who.checkPassword(password, username)) {
+ if (PasswordVerifier.checkPassword(who.getExternalIds(), username, password)) {
return succeedAuthentication(who);
}
logger.atWarning().withCause(e).log(authenticationFailedMsg(username, req));
diff --git a/java/com/google/gerrit/json/EnumTypeAdapterFactory.java b/java/com/google/gerrit/json/EnumTypeAdapterFactory.java
index dc74f67..21c4891 100644
--- a/java/com/google/gerrit/json/EnumTypeAdapterFactory.java
+++ b/java/com/google/gerrit/json/EnumTypeAdapterFactory.java
@@ -34,7 +34,7 @@
public class EnumTypeAdapterFactory implements TypeAdapterFactory {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
- @SuppressWarnings({"unchecked"})
+ @SuppressWarnings({"rawtypes", "unchecked"})
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
TypeAdapter<T> defaultEnumAdapter = TypeAdapters.ENUM_FACTORY.create(gson, typeToken);
@@ -43,7 +43,7 @@
return null;
}
- return (TypeAdapter<T>) new EnumTypeAdapter(defaultEnumAdapter, typeToken);
+ return new EnumTypeAdapter(defaultEnumAdapter, typeToken);
}
private static class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> {
diff --git a/java/com/google/gerrit/server/account/AccountState.java b/java/com/google/gerrit/server/account/AccountState.java
index 8555166..6eb6ca1 100644
--- a/java/com/google/gerrit/server/account/AccountState.java
+++ b/java/com/google/gerrit/server/account/AccountState.java
@@ -14,13 +14,9 @@
package com.google.gerrit.server.account;
-import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
-
import com.google.common.base.MoreObjects;
-import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.client.DiffPreferencesInfo;
import com.google.gerrit.extensions.client.EditPreferencesInfo;
@@ -34,7 +30,6 @@
import java.io.IOException;
import java.util.Collection;
import java.util.Optional;
-import org.apache.commons.codec.DecoderException;
import org.eclipse.jgit.lib.ObjectId;
/**
@@ -45,8 +40,6 @@
* account cache (see {@link AccountCache#get(Account.Id)}).
*/
public class AccountState {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
/**
* Creates an AccountState from the given account config.
*
@@ -175,29 +168,6 @@
return userName;
}
- public boolean checkPassword(@Nullable String password, String username) {
- if (password == null) {
- return false;
- }
- for (ExternalId id : getExternalIds()) {
- // Only process the "username:$USER" entry, which is unique.
- if (!id.isScheme(SCHEME_USERNAME) || !username.equals(id.key().id())) {
- continue;
- }
-
- String hashedStr = id.password();
- if (!Strings.isNullOrEmpty(hashedStr)) {
- try {
- return HashedPassword.decode(hashedStr).checkPassword(password);
- } catch (DecoderException e) {
- logger.atSevere().log("DecoderException for user %s: %s ", username, e.getMessage());
- return false;
- }
- }
- }
- return false;
- }
-
/** The external identities that identify the account holder. */
public ImmutableSet<ExternalId> getExternalIds() {
return externalIds;
diff --git a/java/com/google/gerrit/server/account/externalids/PasswordVerifier.java b/java/com/google/gerrit/server/account/externalids/PasswordVerifier.java
new file mode 100644
index 0000000..e4bf27b
--- /dev/null
+++ b/java/com/google/gerrit/server/account/externalids/PasswordVerifier.java
@@ -0,0 +1,54 @@
+// Copyright (C) 2019 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.server.account.externalids;
+
+import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
+
+import com.google.common.base.Strings;
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.server.account.HashedPassword;
+import java.util.Collection;
+import org.apache.commons.codec.DecoderException;
+
+/** Checks if a given username and password match a user's external IDs. */
+public class PasswordVerifier {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ /** Returns {@code true} if there is an external ID matching both the username and password. */
+ public static boolean checkPassword(
+ Collection<ExternalId> externalIds, String username, @Nullable String password) {
+ if (password == null) {
+ return false;
+ }
+ for (ExternalId id : externalIds) {
+ // Only process the "username:$USER" entry, which is unique.
+ if (!id.isScheme(SCHEME_USERNAME) || !username.equals(id.key().id())) {
+ continue;
+ }
+
+ String hashedStr = id.password();
+ if (!Strings.isNullOrEmpty(hashedStr)) {
+ try {
+ return HashedPassword.decode(hashedStr).checkPassword(password);
+ } catch (DecoderException e) {
+ logger.atSevere().log("DecoderException for user %s: %s ", username, e.getMessage());
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/java/com/google/gerrit/server/auth/InternalAuthBackend.java b/java/com/google/gerrit/server/auth/InternalAuthBackend.java
index c06c66b..2821bf6 100644
--- a/java/com/google/gerrit/server/auth/InternalAuthBackend.java
+++ b/java/com/google/gerrit/server/auth/InternalAuthBackend.java
@@ -16,6 +16,7 @@
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.account.externalids.PasswordVerifier;
import com.google.gerrit.server.config.AuthConfig;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -62,7 +63,7 @@
+ ": account inactive or not provisioned in Gerrit");
}
- if (!who.checkPassword(req.getPassword().get(), username)) {
+ if (!PasswordVerifier.checkPassword(who.getExternalIds(), username, req.getPassword().get())) {
throw new InvalidCredentialsException();
}
return new AuthUser(AuthUser.UUID.create(username), username);
diff --git a/javatests/com/google/gerrit/json/JsonEnumMappingTest.java b/javatests/com/google/gerrit/json/JsonEnumMappingTest.java
index 6e57b01..dd710f9 100644
--- a/javatests/com/google/gerrit/json/JsonEnumMappingTest.java
+++ b/javatests/com/google/gerrit/json/JsonEnumMappingTest.java
@@ -71,10 +71,6 @@
private static class TestData {
TestEnum value;
-
- public TestData(TestEnum value) {
- this.value = value;
- }
}
private enum TestEnum {
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
index 077af1f..8500e3b 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
@@ -159,6 +159,11 @@
},
_computePreferences(account, preferences) {
+ // Polymer 2: check for undefined
+ if ([account, preferences].some(arg => arg === undefined)) {
+ return;
+ }
+
this.changeTableColumns = this.columnNames;
if (account) {
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
index 90641df..54e2edd 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
@@ -590,6 +590,15 @@
_actionsChanged(actionsChangeRecord, revisionActionsChangeRecord,
additionalActionsChangeRecord) {
+ // Polymer 2: check for undefined
+ if ([
+ actionsChangeRecord,
+ revisionActionsChangeRecord,
+ additionalActionsChangeRecord,
+ ].some(arg => arg === undefined)) {
+ return;
+ }
+
const additionalActions = (additionalActionsChangeRecord &&
additionalActionsChangeRecord.base) || [];
this.hidden = this._keyCount(actionsChangeRecord) === 0 &&
@@ -620,6 +629,15 @@
_editStatusChanged(editMode, editPatchsetLoaded,
editBasedOnCurrentPatchSet, disableEdit) {
+ // Polymer 2: check for undefined
+ if ([
+ editMode,
+ editBasedOnCurrentPatchSet,
+ disableEdit,
+ ].some(arg => arg === undefined)) {
+ return;
+ }
+
if (disableEdit) {
this._deleteAndNotify('publishEdit');
this._deleteAndNotify('rebaseEdit');
@@ -1344,6 +1362,17 @@
*/
_computeAllActions(changeActionsRecord, revisionActionsRecord,
primariesRecord, additionalActionsRecord, change) {
+ // Polymer 2: check for undefined
+ if ([
+ changeActionsRecord,
+ revisionActionsRecord,
+ primariesRecord,
+ additionalActionsRecord,
+ change,
+ ].some(arg => arg === undefined)) {
+ return [];
+ }
+
const revisionActionValues = this._getActionValues(revisionActionsRecord,
primariesRecord, additionalActionsRecord, ActionType.REVISION);
const changeActionValues = this._getActionValues(changeActionsRecord,
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
index 154fc36..2b6528a 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
@@ -438,7 +438,7 @@
},
_computeParents(change) {
- if (!change.current_revision ||
+ if (!change || !change.current_revision ||
!change.revisions[change.current_revision] ||
!change.revisions[change.current_revision].commit) {
return undefined;
@@ -447,13 +447,13 @@
},
_computeParentsLabel(parents) {
- return parents.length > 1 ? 'Parents' : 'Parent';
+ return parents && parents.length > 1 ? 'Parents' : 'Parent';
},
_computeParentListClass(parents, parentIsCurrent) {
return [
'parentList',
- parents.length > 1 ? 'merge' : 'nonMerge',
+ parents && parents.length > 1 ? 'merge' : 'nonMerge',
parentIsCurrent ? 'current' : 'notCurrent',
].join(' ');
},
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 3a5f326..05bbb5b 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -60,6 +60,7 @@
};
const CHANGE_DATA_TIMING_LABEL = 'ChangeDataLoaded';
+ const CHANGE_RELOAD_TIMING_LABEL = 'ChangeReloaded';
const SEND_REPLY_TIMING_LABEL = 'SendReply';
Polymer({
@@ -303,6 +304,7 @@
'_labelsChanged(_change.labels.*)',
'_paramsAndChangeChanged(params, _change)',
'_patchNumChanged(_patchRange.patchNum)',
+ '_loadDynamicTabHeaderAndContent(_change, _selectedRevision)',
],
keyboardShortcuts() {
@@ -337,17 +339,6 @@
this._setDiffViewMode();
});
- Gerrit.awaitPluginsLoaded().then(() => {
- this._dynamicTabHeaderEndpoints =
- Gerrit._endpoints.getDynamicEndpoints('change-view-tab-header');
- this._dynamicTabContentEndpoints =
- Gerrit._endpoints.getDynamicEndpoints('change-view-tab-content');
- if (this._dynamicTabContentEndpoints.length
- !== this._dynamicTabHeaderEndpoints.length) {
- console.warn('Different number of tab headers and tab content.');
- }
- });
-
this.addEventListener('comment-save', this._handleCommentSave.bind(this));
this.addEventListener('comment-refresh', this._reloadDrafts.bind(this));
this.addEventListener('comment-discard',
@@ -770,7 +761,47 @@
});
},
- _paramsAndChangeChanged(value) {
+ /**
+ * We use an observer to observe 'change' and 'selectedRevision'
+ * variables. This fixes an issue under Polymer 2 so that the dynamic
+ * plugins loads when these variables load.
+ */
+ _loadDynamicTabHeaderAndContent(change, selectedRevision) {
+ // These vars are unused, but because primaryTabs extension point
+ // uses it, we makes sure we doin't load the plugin until these vars
+ // exist.
+ if (!change || !selectedRevision) return;
+
+ // We cache the _dynamicTabHeaderEndpoints and _dynamicTabContentEndpoints
+ // var so that we doin't keep loading the same dynamic plugin
+ // over and over when 'change' or 'selectedRevision' change.
+ if (this._dynamicTabHeaderEndpoints || this._dynamicTabContentEndpoints) {
+ return;
+ }
+
+ Gerrit.awaitPluginsLoaded().then(() => {
+ this._dynamicTabHeaderEndpoints =
+ Gerrit._endpoints.getDynamicEndpoints('change-view-tab-header');
+ this._dynamicTabContentEndpoints =
+ Gerrit._endpoints.getDynamicEndpoints('change-view-tab-content');
+ if (this._dynamicTabContentEndpoints.length
+ !== this._dynamicTabHeaderEndpoints.length) {
+ console.warn('Different number of tab headers and tab content.');
+ }
+ }).then(() => {
+ // We need a second then(..) to ensure that the dynamic endpoints
+ // are created before we call _performPostLoadTasks(). This ensures it has
+ // enough time before the primary tab gets selected.
+ this._performPostLoadTasks();
+ });
+ },
+
+ _paramsAndChangeChanged(value, change) {
+ // Polymer 2: check for undefined
+ if ([value, change].some(arg => arg === undefined)) {
+ return;
+ }
+
// If the change number or patch range is different, then reset the
// selected file index.
const patchRangeState = this.viewState.patchRange;
@@ -909,8 +940,8 @@
return 'PARENT';
},
- _computeShowPrimaryTabs(dynamicTabContentEndpoints) {
- return dynamicTabContentEndpoints.length > 0;
+ _computeShowPrimaryTabs(dynamicTabHeaderEndpoints) {
+ return dynamicTabHeaderEndpoints && dynamicTabHeaderEndpoints.length > 0;
},
_computeChangeUrl(change) {
@@ -1358,15 +1389,17 @@
/**
* Reload the change.
- * @param {boolean=} opt_reloadRelatedChanges Reloads the related chanegs
- * when true.
+ * @param {boolean=} opt_isLocationChange Reloads the related changes
+ * when true and ends reporting events that started on location change.
* @return {Promise} A promise that resolves when the core data has loaded.
* Some non-core data loading may still be in-flight when the core data
* promise resolves.
*/
- _reload(opt_reloadRelatedChanges) {
+ _reload(opt_isLocationChange) {
this._loading = true;
this._relatedChangesCollapsed = true;
+ this.$.reporting.time(CHANGE_RELOAD_TIMING_LABEL);
+ this.$.reporting.time(CHANGE_DATA_TIMING_LABEL);
// Array to house all promises related to data requests.
const allDataPromises = [];
@@ -1380,7 +1413,12 @@
// change content may start appearing.
const loadingFlagSet = detailCompletes
.then(() => { this._loading = false; })
- .then(() => { this.$.reporting.changeDisplayed(); });
+ .then(() => {
+ this.$.reporting.timeEnd(CHANGE_RELOAD_TIMING_LABEL);
+ if (opt_isLocationChange) {
+ this.$.reporting.changeDisplayed();
+ }
+ });
// Resolves when the project config has loaded.
const projectConfigLoaded = detailCompletes
@@ -1440,16 +1478,17 @@
coreDataPromise = mergeabilityLoaded;
}
- if (opt_reloadRelatedChanges) {
+ if (opt_isLocationChange) {
const relatedChangesLoaded = coreDataPromise
.then(() => this.$.relatedChanges.reload());
allDataPromises.push(relatedChangesLoaded);
}
- this.$.reporting.time(CHANGE_DATA_TIMING_LABEL);
Promise.all(allDataPromises).then(() => {
this.$.reporting.timeEnd(CHANGE_DATA_TIMING_LABEL);
- this.$.reporting.changeFullyLoaded();
+ if (opt_isLocationChange) {
+ this.$.reporting.changeFullyLoaded();
+ }
});
return coreDataPromise;
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
index e8de93b..79d8d93 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
@@ -1844,5 +1844,50 @@
MockInteractions.tap(element.$.changeStar.$$('button'));
assert.isTrue(stub.called);
});
+
+ suite('gr-reporting tests', () => {
+ setup(() => {
+ element._patchRange = {
+ basePatchNum: 'PARENT',
+ patchNum: 1,
+ };
+ sandbox.stub(element, '_getChangeDetail').returns(Promise.resolve());
+ sandbox.stub(element, '_getProjectConfig').returns(Promise.resolve());
+ sandbox.stub(element, '_reloadComments').returns(Promise.resolve());
+ sandbox.stub(element, '_getMergeability').returns(Promise.resolve());
+ sandbox.stub(element, '_getLatestCommitMessage')
+ .returns(Promise.resolve());
+ });
+
+ test('don\'t report changedDisplayed on reply', done => {
+ const changeDisplayStub =
+ sandbox.stub(element.$.reporting, 'changeDisplayed');
+ const changeFullyLoadedStub =
+ sandbox.stub(element.$.reporting, 'changeFullyLoaded');
+ element._handleReplySent();
+ flush(() => {
+ assert.isFalse(changeDisplayStub.called);
+ assert.isFalse(changeFullyLoadedStub.called);
+ done();
+ });
+ });
+
+ test('report changedDisplayed on _paramsChanged', done => {
+ const changeDisplayStub =
+ sandbox.stub(element.$.reporting, 'changeDisplayed');
+ const changeFullyLoadedStub =
+ sandbox.stub(element.$.reporting, 'changeFullyLoaded');
+ element._paramsChanged({
+ view: Gerrit.Nav.View.CHANGE,
+ changeNum: 101,
+ project: 'test-project',
+ });
+ flush(() => {
+ assert.isTrue(changeDisplayStub.called);
+ assert.isTrue(changeFullyLoadedStub.called);
+ done();
+ });
+ });
+ });
});
</script>
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
index f4523cf..2f098de 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
@@ -60,6 +60,15 @@
],
_computeMessage(changeStatus, commitNum, commitMessage) {
+ // Polymer 2: check for undefined
+ if ([
+ changeStatus,
+ commitNum,
+ commitMessage,
+ ].some(arg => arg === undefined)) {
+ return;
+ }
+
let newMessage = commitMessage;
if (changeStatus === 'MERGED') {
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js
index 1c3bcfb..22ce27e 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js
@@ -144,6 +144,11 @@
* the corresponding value to be submitted.
*/
_updateSelectedOption(rebaseOnCurrent, hasParent) {
+ // Polymer 2: check for undefined
+ if ([rebaseOnCurrent, hasParent].some(arg => arg === undefined)) {
+ return;
+ }
+
if (this._displayParentOption(rebaseOnCurrent, hasParent)) {
this.$.rebaseOnParentInput.checked = true;
} else if (this._displayTipOption(rebaseOnCurrent, hasParent)) {
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
index a802fb9..aff1e0a 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
@@ -137,6 +137,11 @@
},
_computePatchSetDescription(change, patchNum) {
+ // Polymer 2: check for undefined
+ if ([change, patchNum].some(arg => arg === undefined)) {
+ return;
+ }
+
const rev = this.getRevisionByPatchNum(change.revisions, patchNum);
this._patchsetDescription = (rev && rev.description) ?
rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : '';
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 4f160e4..1620610 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -817,6 +817,17 @@
},
_computeFiles(filesByPath, changeComments, patchRange, reviewed, loading) {
+ // Polymer 2: check for undefined
+ if ([
+ filesByPath,
+ changeComments,
+ patchRange,
+ reviewed,
+ loading,
+ ].some(arg => arg === undefined)) {
+ return;
+ }
+
// Await all promises resolving from reload. @See Issue 9057
if (loading || !changeComments) { return; }
@@ -904,6 +915,11 @@
},
_computePatchSetDescription(revisions, patchNum) {
+ // Polymer 2: check for undefined
+ if ([revisions, patchNum].some(arg => arg === undefined)) {
+ return '';
+ }
+
const rev = this.getRevisionByPatchNum(revisions, patchNum);
return (rev && rev.description) ?
rev.description.substring(0, PATCH_DESC_MAX_LENGTH) : '';
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
index eeec437..4166511 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
@@ -292,6 +292,17 @@
_resultsChanged(related, submittedTogether, conflicts,
cherryPicks, sameTopic) {
+ // Polymer 2: check for undefined
+ if ([
+ related,
+ submittedTogether,
+ conflicts,
+ cherryPicks,
+ sameTopic,
+ ].some(arg => arg === undefined)) {
+ return;
+ }
+
const results = [
related && related.changes,
submittedTogether && submittedTogether.changes,
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
index cd42606..640cbc8 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
@@ -623,6 +623,11 @@
},
_changeUpdated(changeRecord, owner) {
+ // Polymer 2: check for undefined
+ if ([changeRecord, owner].some(arg => arg === undefined)) {
+ return;
+ }
+
this._rebuildReviewerArrays(changeRecord.base, owner);
},
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
index 43ea7f0..63e0647 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
@@ -165,6 +165,11 @@
},
_reviewersChanged(changeRecord, owner) {
+ // Polymer 2: check for undefined
+ if ([changeRecord, owner].some(arg => arg === undefined)) {
+ return;
+ }
+
let result = [];
const reviewers = changeRecord.base;
for (const key in reviewers) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
index 2c14609..bce3d07 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
@@ -742,6 +742,15 @@
_whitespaceChanged(
preferredWhitespaceLevel, loadedWhitespaceLevel,
noRenderOnPrefsChange) {
+ // Polymer 2: check for undefined
+ if ([
+ preferredWhitespaceLevel,
+ loadedWhitespaceLevel,
+ noRenderOnPrefsChange,
+ ].some(arg => arg === undefined)) {
+ return;
+ }
+
if (preferredWhitespaceLevel !== loadedWhitespaceLevel &&
!noRenderOnPrefsChange) {
this.reload();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index d604df2..0c1facd 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -260,6 +260,11 @@
},
_getFiles(changeNum, patchRangeRecord) {
+ // Polymer 2: check for undefined
+ if ([changeNum, patchRangeRecord].some(arg => arg === undefined)) {
+ return;
+ }
+
const patchRange = patchRangeRecord.base;
return this.$.restAPI.getChangeFilePathsAsSpeciallySortedArray(
changeNum, patchRange).then(files => {
@@ -680,6 +685,11 @@
},
_setReviewedObserver(_loggedIn, paramsRecord, _prefs) {
+ // Polymer 2: check for undefined
+ if ([_loggedIn, paramsRecord, _prefs].some(arg => arg === undefined)) {
+ return;
+ }
+
const params = paramsRecord.base || {};
if (!_loggedIn) { return; }
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
index 1bafe48..c69b313 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -297,6 +297,11 @@
},
_enableSelectionObserver(loggedIn, isAttached) {
+ // Polymer 2: check for undefined
+ if ([loggedIn, isAttached].some(arg => arg === undefined)) {
+ return;
+ }
+
if (loggedIn && isAttached) {
this.listen(document, 'selectionchange', '_handleSelectionChange');
this.listen(document, 'mouseup', '_handleMouseUp');
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
index 5603391..7f0a754 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
@@ -159,6 +159,9 @@
],
attached() {
+ // Polymer 2: anchor tag won't work on shadow DOM
+ // we need to manually calling scrollIntoView when hash changed
+ this.listen(window, 'location-change', '_handleLocationChange');
this.fire('title-change', {title: 'Settings'});
this._isDark = !!window.localStorage.getItem('dark-theme');
@@ -215,9 +218,28 @@
this._loadingPromise = Promise.all(promises).then(() => {
this._loading = false;
+
+ // Handle anchor tag for initial load
+ this._handleLocationChange();
});
},
+ detached() {
+ this.unlisten(window, 'location-change', '_handleLocationChange');
+ },
+
+ _handleLocationChange() {
+ // Handle anchor tag after dom attached
+ const urlHash = window.location.hash;
+ if (urlHash) {
+ // Use shadowRoot for Polymer 2
+ const elem = (this.shadowRoot || document).querySelector(urlHash);
+ if (elem) {
+ elem.scrollIntoView();
+ }
+ }
+ },
+
reloadAccountDetail() {
Promise.all([
this.$.accountInfo.loadData(),
diff --git a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.html b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.html
index 7f71c89..2f2c7ba 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.html
@@ -40,14 +40,8 @@
:host([show-avatar]) .container {
padding-left: 0;
}
- gr-button.remove:hover,
- gr-button.remove:focus {
- --gr-button: {
- color: #333;
- }
- }
gr-button.remove {
- --gr-button: {
+ --gr-remove-button-style: {
border: 0;
color: var(--deemphasized-text-color);
font-size: 1.7rem;
@@ -59,6 +53,19 @@
text-decoration: none;
}
}
+
+ gr-button.remove:hover,
+ gr-button.remove:focus {
+ --gr-button: {
+ @apply --gr-remove-button-style
+ color: #333;
+ }
+ }
+ gr-button.remove {
+ --gr-button: {
+ @apply --gr-remove-button-style
+ }
+ }
:host:focus {
border-color: transparent;
box-shadow: none;
diff --git a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html
index 2b043c4..cc6453c 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.html
@@ -35,7 +35,6 @@
box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
color: var(--view-background-color);
left: 1.25rem;
- padding: 1em 1.5em;
position: fixed;
transform: translateY(5rem);
transition: transform var(--gr-alert-transition-duration, 80ms) ease-out;
@@ -44,6 +43,17 @@
:host([shown]) {
transform: translateY(0);
}
+ /**
+ * NOTE: To avoid style being overwritten by outside of the shadow DOM
+ * (as outside styles always win), .content-wrapper is introduced as a
+ * wrapper around main content to have better encapsulation, styles that
+ * may be affected by outside should be defined on it.
+ * In this case, `padding:0px` is defined in main.css for all elements
+ * with the universal selector: *.
+ */
+ .content-wrapper {
+ padding: 1em 1.5em;
+ }
.text {
color: var(--tooltip-text-color);
display: inline-block;
@@ -62,12 +72,14 @@
}
}
</style>
- <span class="text">[[text]]</span>
- <gr-button
- link
- class="action"
- hidden$="[[_hideActionButton]]"
- on-tap="_handleActionTap">[[actionText]]</gr-button>
+ <div class="content-wrapper">
+ <span class="text">[[text]]</span>
+ <gr-button
+ link
+ class="action"
+ hidden$="[[_hideActionButton]]"
+ on-tap="_handleActionTap">[[actionText]]</gr-button>
+ </div>
</template>
<script src="gr-alert.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
index b74e48c..46877d7 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
@@ -76,14 +76,31 @@
on-focus="_onInputFocus"
on-blur="_onInputBlur"
autocomplete="off">
- <!-- slot is for future use (2.x) while prefix attribute is for 1.x
- (current) -->
- <iron-icon
- icon="gr-icons:search"
- slot="prefix"
+
+ <template is="dom-if" if="[[_isPolymer2()]]">
+ <!-- Content uses vertical-align:baseline. If iron-icon is placed
+ directly in the slot and is not visible, vertical-align doesn't
+ work, because display:none convert it from inline-block element to
+ block element. To fix this problem, iron-icon is wrapped in div
+ which never changes display type.
+ The problem doesn't exist in Polymer1 because DOM is different.
+ Applying the same fix to Polymer1 breaks vertical-align.
+ -->
+ <div slot="prefix">
+ <iron-icon
+ icon="gr-icons:search"
+ class$="searchIcon [[_computeShowSearchIconClass(showSearchIcon)]]">
+ </iron-icon>
+ </div>
+ </template>
+ <template is="dom-if" if="[[!_isPolymer2()]]">
+ <iron-icon
prefix
+ icon="gr-icons:search"
class$="searchIcon [[_computeShowSearchIconClass(showSearchIcon)]]">
- </iron-icon>
+ </iron-icon>
+ </template>
+
</paper-input>
<gr-autocomplete-dropdown
vertical-align="top"
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
index c36ec21..af31473 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
@@ -242,8 +242,13 @@
},
_updateSuggestions(text, threshold, noDebounce) {
+ // Polymer 2: check for undefined
+ if ([text, threshold, noDebounce].some(arg => arg === undefined)) {
+ return;
+ }
+
if (this._disableSuggestions) { return; }
- if (text === undefined || text.length < threshold) {
+ if (text.length < threshold) {
this._suggestions = [];
this.value = '';
return;
@@ -420,5 +425,9 @@
_computeShowSearchIconClass(showSearchIcon) {
return showSearchIcon ? 'showSearchIcon' : '';
},
+
+ _isPolymer2() {
+ return window.POLYMER2;
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
index 217321f..cff35b4 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
@@ -320,12 +320,15 @@
});
});
- test('search icon shows with showSearchIcon property', () => {
- assert.equal(getComputedStyle(element.$$('iron-icon')).display,
- 'none');
- element.showSearchIcon = true;
- assert.notEqual(getComputedStyle(element.$$('iron-icon')).display,
- 'none');
+ test('search icon shows with showSearchIcon property', done => {
+ flush(() => {
+ assert.equal(getComputedStyle(element.$$('iron-icon')).display,
+ 'none');
+ element.showSearchIcon = true;
+ assert.notEqual(getComputedStyle(element.$$('iron-icon')).display,
+ 'none');
+ done();
+ });
});
test('vertical offset overridden by param if it exists', () => {
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
index 754c3c0..340297f 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
@@ -39,6 +39,37 @@
text-transform: none;
}
paper-button {
+ /* The next lines contains a copy of paper-button style.
+ Without a copy, the @apply works incorrectly with Polymer 2.
+ @apply is deprecated and is not recommended to use. It is expected
+ that @apply will be replaced with the ::part CSS pseudo-element.
+ After replacecment copied lines can be removed.
+ */
+ @apply --layout-inline;
+ @apply --layout-center-center;
+ position: relative;
+ box-sizing: border-box;
+ min-width: 5.14em;
+ margin: 0 0.29em;
+ background: transparent;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-tap-highlight-color: transparent;
+ font: inherit;
+ text-transform: uppercase;
+ outline-width: 0;
+ border-radius: 3px;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ cursor: pointer;
+ z-index: 0;
+ padding: 0.7em 0.57em;
+
+ @apply --paper-font-common-base;
+ @apply --paper-button;
+ /* End of copy*/
+
/* paper-button sets this to anti-aliased, which appears different than
bold font elsewhere on macOS. */
-webkit-font-smoothing: initial;
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
index 57835a3..cf2f57d 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
@@ -211,6 +211,11 @@
},
_calculateActionstoShow(showActions, isRobotComment) {
+ // Polymer 2: check for undefined
+ if ([showActions, isRobotComment].some(arg => arg === undefined)) {
+ return;
+ }
+
this._showHumanActions = showActions && !isRobotComment;
this._showRobotActions = showActions && isRobotComment;
},
@@ -601,6 +606,11 @@
},
_loadLocalDraft(changeNum, patchNum, comment) {
+ // Polymer 2: check for undefined
+ if ([changeNum, patchNum, comment].some(arg => arg === undefined)) {
+ return;
+ }
+
// Only apply local drafts to comments that haven't been saved
// remotely, and haven't been given a default message already.
//
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js
index bcf3729..b6d909d 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js
@@ -104,6 +104,11 @@
},
_handleValueChange(value, items) {
+ // Polymer 2: check for undefined
+ if ([value, items].some(arg => arg === undefined)) {
+ return;
+ }
+
if (!value) { return; }
const selectedObj = items.find(item => {
return item.value + '' === value + '';
diff --git a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js
index 44a8791..479dd1e 100644
--- a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js
@@ -66,6 +66,11 @@
* enabled.
*/
_updateTitle(text, limit, tooltipLimit) {
+ // Polymer 2: check for undefined
+ if ([text, limit, tooltipLimit].some(arg => arg === undefined)) {
+ return;
+ }
+
this.hasTooltip = !!limit && !!text && text.length > limit;
if (this.hasTooltip) {
this.setAttribute('title', text.substr(0, tooltipLimit));
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html
index f6332f4..939d582 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.html
@@ -37,14 +37,8 @@
display: inline-flex;
padding: 0 .5em;
}
- gr-button.remove:hover,
- gr-button.remove:focus {
- --gr-button: {
- color: #333;
- }
- }
gr-button.remove {
- --gr-button: {
+ --gr-remove-button-style: {
border: 0;
color: var(--deemphasized-text-color);
font-size: 1.7rem;
@@ -56,6 +50,19 @@
text-decoration: none;
}
}
+
+ gr-button.remove:hover,
+ gr-button.remove:focus {
+ --gr-button: {
+ @apply --gr-remove-button-style
+ color: #333;
+ }
+ }
+ gr-button.remove {
+ --gr-button: {
+ @apply --gr-remove-button-style
+ }
+ }
.transparentBackground,
gr-button.transparentBackground {
background-color: transparent;
diff --git a/polygerrit-ui/app/styles/gr-voting-styles.html b/polygerrit-ui/app/styles/gr-voting-styles.html
index 3b1ee64..eec79be 100644
--- a/polygerrit-ui/app/styles/gr-voting-styles.html
+++ b/polygerrit-ui/app/styles/gr-voting-styles.html
@@ -23,6 +23,7 @@
border: 1px solid rgba(0,0,0,.12);
border-radius: 1em;
box-shadow: none;
+ box-sizing: border-box;
min-width: 3em;
}
}
diff --git a/polygerrit-ui/server.go b/polygerrit-ui/server.go
index e849469..305a6b8 100644
--- a/polygerrit-ui/server.go
+++ b/polygerrit-ui/server.go
@@ -103,7 +103,7 @@
func handleIndex(writer http.ResponseWriter, originalRequest *http.Request) {
fakeRequest := &http.Request{
URL: &url.URL{
- Path: "/",
+ Path: "/",
RawQuery: originalRequest.URL.RawQuery,
},
}
@@ -170,7 +170,7 @@
func patchResponse(req *http.Request, res *http.Response) io.Reader {
switch req.URL.EscapedPath() {
case "/":
- return replaceCdn(res.Body)
+ return rewriteHostPage(res.Body)
case "/config/server/info":
return injectLocalPlugins(res.Body)
default:
@@ -178,13 +178,39 @@
}
}
-func replaceCdn(reader io.Reader) io.Reader {
+func rewriteHostPage(reader io.Reader) io.Reader {
buf := new(bytes.Buffer)
buf.ReadFrom(reader)
original := buf.String()
+ // Simply remove all CDN references, so files are loaded from the local file system or the proxy
+ // server instead.
replaced := cdnPattern.ReplaceAllString(original, "")
+ // Modify window.INITIAL_DATA so that it has the same effect as injectLocalPlugins. To achieve
+ // this let's add JavaScript lines at the end of the <script>...</script> snippet that also
+ // contains window.INITIAL_DATA=...
+ // Here we rely on the fact that the <script> snippet that we want to append to is the first one.
+ if len(*plugins) > 0 {
+ insertionPoint := strings.Index(replaced, "</script>")
+ builder := new(strings.Builder)
+ builder.WriteString(
+ "window.INITIAL_DATA['/config/server/info'].plugin.html_resource_paths = []; ")
+ builder.WriteString(
+ "window.INITIAL_DATA['/config/server/info'].plugin.js_resource_paths = []; ")
+ for _, p := range strings.Split(*plugins, ",") {
+ if filepath.Ext(p) == ".html" {
+ builder.WriteString(
+ "window.INITIAL_DATA['/config/server/info'].plugin.html_resource_paths.push('" + p + "'); ")
+ }
+ if filepath.Ext(p) == ".js" {
+ builder.WriteString(
+ "window.INITIAL_DATA['/config/server/info'].plugin.js_resource_paths.push('" + p + "'); ")
+ }
+ }
+ replaced = replaced[:insertionPoint] + builder.String() + replaced[insertionPoint:]
+ }
+
return strings.NewReader(replaced)
}
@@ -209,11 +235,11 @@
jsResources := getJsonPropByPath(response, jsPluginsPath).([]interface{})
for _, p := range strings.Split(*plugins, ",") {
- if strings.HasSuffix(p, ".html") {
+ if filepath.Ext(p) == ".html" {
htmlResources = append(htmlResources, p)
}
- if strings.HasSuffix(p, ".js") {
+ if filepath.Ext(p) == ".js" {
jsResources = append(jsResources, p)
}
}
@@ -263,7 +289,7 @@
// Any path prefixes that should resolve to index.html.
var (
- fePaths = []string{"/q/", "/c/", "/p/", "/x/", "/dashboard/", "/admin/"}
+ fePaths = []string{"/q/", "/c/", "/p/", "/x/", "/dashboard/", "/admin/", "/settings/"}
issueNumRE = regexp.MustCompile(`^\/\d+\/?$`)
)