Merge "Fix browsing groups in web UI"
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js
index 2d66cfa..0ec3d6a 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js
@@ -50,7 +50,11 @@
       return url.pathname;
     }
     const base = Gerrit.BaseUrlBehavior.getBaseUrl();
-    const pathname = url.pathname.replace(base, '');
+    let pathname = url.pathname.replace(base, '');
+    // Load from ASSETS_PATH
+    if (window.ASSETS_PATH && url.href.includes(window.ASSETS_PATH)) {
+      pathname = url.href.replace(window.ASSETS_PATH, '');
+    }
     // Site theme is server from predefined path.
     if (pathname === '/static/gerrit-theme.html') {
       return 'gerrit-theme';
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html
index 128738d..b43796f 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils_test.html
@@ -72,6 +72,15 @@
             'gerrit-theme'
         );
       });
+
+      test('with ASSETS_PATH', () => {
+        window.ASSETS_PATH = 'http://cdn.com/2';
+        assert.equal(
+            getPluginNameFromUrl(`${window.ASSETS_PATH}/plugins/a.html`),
+            'a'
+        );
+        window.ASSETS_PATH = undefined;
+      });
     });
   });
 </script>
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js
index 4be38b6..96d8411 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js
@@ -113,7 +113,7 @@
       this._pluginListLoaded = true;
 
       plugins.forEach(path => {
-        const url = this._urlFor(path);
+        const url = this._urlFor(path, window.ASSETS_PATH);
         // Skip if preloaded, for bundling.
         if (this.isPluginPreloaded(url)) return;
 
@@ -128,11 +128,11 @@
         });
 
         if (this._isPathEndsWith(url, '.html')) {
-          this._importHtmlPlugin(url, opts && opts[path]);
+          this._importHtmlPlugin(path, opts && opts[path]);
         } else if (this._isPathEndsWith(url, '.js')) {
-          this._loadJsPlugin(url);
+          this._loadJsPlugin(path);
         } else {
-          this._failToLoad(`Unrecognized plugin url ${url}`, url);
+          this._failToLoad(`Unrecognized plugin path ${path}`, path);
         }
       });
 
@@ -181,14 +181,15 @@
         return;
       }
 
-      const pluginObject = this.getPlugin(src);
+      const url = this._urlFor(src);
+      const pluginObject = this.getPlugin(url);
       let plugin = pluginObject && pluginObject.plugin;
       if (!plugin) {
-        plugin = new Plugin(src);
+        plugin = new Plugin(url);
       }
       try {
         callback(plugin);
-        this._pluginInstalled(src, plugin);
+        this._pluginInstalled(url, plugin);
       } catch (e) {
         this._failToLoad(`${e.name}: ${e.message}`, src);
       }
@@ -313,38 +314,79 @@
     }
 
     _importHtmlPlugin(pluginUrl, opts = {}) {
-      // onload (second param) needs to be a function. When null or undefined
-      // were passed, plugins were not loaded correctly.
+      const urlWithAP = this._urlFor(pluginUrl, window.ASSETS_PATH);
+      const urlWithoutAP = this._urlFor(pluginUrl);
+      let onerror = null;
+      if (urlWithAP !== urlWithoutAP) {
+        onerror = () => this._loadHtmlPlugin(urlWithoutAP, opts.sync);
+      }
+      this._loadHtmlPlugin(urlWithAP, opts.sync, onerror);
+    }
+
+    _loadHtmlPlugin(url, sync, onerror) {
+      if (!onerror) {
+        onerror = () => {
+          this._failToLoad(`${pluginUrl} import error`, pluginUrl);
+        };
+      }
+
       (Polymer.importHref || Polymer.Base.importHref)(
-          this._urlFor(pluginUrl), () => {},
-          () => this._failToLoad(`${pluginUrl} import error`, pluginUrl),
-          !opts.sync);
+          url, () => {},
+          onerror,
+          !sync);
     }
 
     _loadJsPlugin(pluginUrl) {
-      this._createScriptTag(this._urlFor(pluginUrl));
+      const urlWithAP = this._urlFor(pluginUrl, window.ASSETS_PATH);
+      const urlWithoutAP = this._urlFor(pluginUrl);
+      let onerror = null;
+      if (urlWithAP !== urlWithoutAP) {
+        onerror = () => this._createScriptTag(urlWithoutAP);
+      }
+
+      this._createScriptTag(urlWithAP, onerror);
     }
 
-    _createScriptTag(url) {
+    _createScriptTag(url, onerror) {
+      if (!onerror) {
+        onerror = () => this._failToLoad(`${url} load error`, url);
+      }
+
       const el = document.createElement('script');
       el.defer = true;
       el.setAttribute('src', url);
-      el.onerror = () => this._failToLoad(`${url} load error`, url);
+      el.onerror = onerror;
       return document.body.appendChild(el);
     }
 
-    _urlFor(pathOrUrl) {
+    _urlFor(pathOrUrl, assetsPath) {
       if (!pathOrUrl) {
         return pathOrUrl;
       }
+
+      // theme is per host, should always load from assetsPath
+      const isThemeFile = pathOrUrl.endsWith('static/gerrit-theme.html');
+      const shouldTryLoadFromAssetsPathFirst = !isThemeFile && assetsPath;
       if (pathOrUrl.startsWith(PRELOADED_PROTOCOL) ||
           pathOrUrl.startsWith('http')) {
         // Plugins are loaded from another domain or preloaded.
+        if (pathOrUrl.includes(location.host)
+          && shouldTryLoadFromAssetsPathFirst) {
+          // if is loading from host server, try replace with cdn when assetsPath provided
+          return pathOrUrl
+              .replace(location.origin, assetsPath);
+        }
         return pathOrUrl;
       }
+
       if (!pathOrUrl.startsWith('/')) {
         pathOrUrl = '/' + pathOrUrl;
       }
+
+      if (shouldTryLoadFromAssetsPathFirst) {
+        return assetsPath + pathOrUrl;
+      }
+
       return window.location.origin + getBaseUrl() + pathOrUrl;
     }
 
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.html
index 8c1ec96..08e7e18 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.html
@@ -325,11 +325,11 @@
       let loadJsPluginStub;
       setup(() => {
         importHtmlPluginStub = sandbox.stub();
-        sandbox.stub(Gerrit._pluginLoader, '_importHtmlPlugin', url => {
+        sandbox.stub(Gerrit._pluginLoader, '_loadHtmlPlugin', url => {
           importHtmlPluginStub(url);
         });
         loadJsPluginStub = sandbox.stub();
-        sandbox.stub(Gerrit._pluginLoader, '_loadJsPlugin', url => {
+        sandbox.stub(Gerrit._pluginLoader, '_createScriptTag', url => {
           loadJsPluginStub(url);
         });
       });
@@ -346,8 +346,8 @@
 
         assert.isTrue(failToLoadStub.calledOnce);
         assert.isTrue(failToLoadStub.calledWithExactly(
-            `Unrecognized plugin url ${url}/foo/bar`,
-            `${url}/foo/bar`
+            'Unrecognized plugin path foo/bar',
+            'foo/bar'
         ));
       });
 
@@ -407,6 +407,72 @@
       });
     });
 
+    suite('With ASSETS_PATH', () => {
+      let importHtmlPluginStub;
+      let loadJsPluginStub;
+      setup(() => {
+        window.ASSETS_PATH = 'https://cdn.com';
+        importHtmlPluginStub = sandbox.stub();
+        sandbox.stub(Gerrit._pluginLoader, '_loadHtmlPlugin', url => {
+          importHtmlPluginStub(url);
+        });
+        loadJsPluginStub = sandbox.stub();
+        sandbox.stub(Gerrit._pluginLoader, '_createScriptTag', url => {
+          loadJsPluginStub(url);
+        });
+      });
+
+      teardown(() => {
+        window.ASSETS_PATH = '';
+      });
+
+      test('Should try load plugins from assets path instead', () => {
+        Gerrit._loadPlugins([
+          'foo/bar.js',
+          'foo/bar.html',
+        ]);
+
+        assert.isTrue(importHtmlPluginStub.calledOnce);
+        assert.isTrue(
+            importHtmlPluginStub.calledWithExactly(`https://cdn.com/foo/bar.html`)
+        );
+        assert.isTrue(loadJsPluginStub.calledOnce);
+        assert.isTrue(
+            loadJsPluginStub.calledWithExactly(`https://cdn.com/foo/bar.js`));
+      });
+
+      test('Should honor original path if exists', () => {
+        Gerrit._loadPlugins([
+          'http://e.com/foo/bar.html',
+          'http://e.com/foo/bar.js',
+        ]);
+
+        assert.isTrue(importHtmlPluginStub.calledOnce);
+        assert.isTrue(
+            importHtmlPluginStub.calledWithExactly(`http://e.com/foo/bar.html`)
+        );
+        assert.isTrue(loadJsPluginStub.calledOnce);
+        assert.isTrue(
+            loadJsPluginStub.calledWithExactly(`http://e.com/foo/bar.js`));
+      });
+
+      test('Should try replace current host with assetsPath', () => {
+        const host = window.location.origin;
+        Gerrit._loadPlugins([
+          `${host}/foo/bar.html`,
+          `${host}/foo/bar.js`,
+        ]);
+
+        assert.isTrue(importHtmlPluginStub.calledOnce);
+        assert.isTrue(
+            importHtmlPluginStub.calledWithExactly(`https://cdn.com/foo/bar.html`)
+        );
+        assert.isTrue(loadJsPluginStub.calledOnce);
+        assert.isTrue(
+            loadJsPluginStub.calledWithExactly(`https://cdn.com/foo/bar.js`));
+      });
+    });
+
     test('adds js plugins will call the body', () => {
       Gerrit._loadPlugins([
         'http://e.com/foo/bar.js',
@@ -489,12 +555,10 @@
 
       test('installing preloaded plugin', () => {
         let plugin;
-        window.ASSETS_PATH = 'http://blips.com/chitz';
         Gerrit.install(p => { plugin = p; }, '0.1', 'preloaded:foo');
         assert.strictEqual(plugin.getPluginName(), 'foo');
         assert.strictEqual(plugin.url('/some/thing.html'),
-            'http://blips.com/chitz/plugins/foo/some/thing.html');
-        delete window.ASSETS_PATH;
+            'preloaded:foo/plugins/foo/some/thing.html');
       });
     });
   });
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
index 6c306d9..0aaeaa1 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
@@ -17,8 +17,6 @@
 (function(window) {
   'use strict';
 
-  const PRELOADED_PROTOCOL = 'preloaded:';
-
   const PANEL_ENDPOINTS_MAPPING = {
     CHANGE_SCREEN_BELOW_COMMIT_INFO_BLOCK: 'change-view-integration',
     CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK: 'change-metadata-item',
@@ -66,13 +64,6 @@
 
     this._url = new URL(opt_url);
     this._name = getPluginNameFromUrl(this._url);
-    if (this._url.protocol === PRELOADED_PROTOCOL) {
-      // Original plugin URL is used in plugin assets URLs calculation.
-      const assetsBaseUrl = window.ASSETS_PATH ||
-          (window.location.origin + Gerrit.BaseUrlBehavior.getBaseUrl());
-      this._url = new URL(assetsBaseUrl + '/plugins/' + this._name +
-          '/static/' + this._name + '.js');
-    }
   }
 
   Plugin._sharedAPIElement = document.createElement('gr-js-api-interface');