Skip to content

Commit

Permalink
iwa: Add buttons to chrome://web-app-internals to update dev proxy IWAs
Browse files Browse the repository at this point in the history
This CL adds a list of all installed dev mode proxy IWAs to
chrome://web-app-internals. Each list item has a button to trigger an
update, which uses the same update system used for normal IWA updates,
except for the update discovery part.
I will look into adding similar functionality for dev-mode bundle apps
later.

Screenshot: https://imgur.com/a/5E3lZUt

Low-Coverage-Reason: OTHER Debug-only code with no existing coverage.
Bug: 1444407
Change-Id: I28a77a961e3736220b285f5742ca316b2aba87d6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4915793
Commit-Queue: Christian Flach <cmfcmf@chromium.org>
Reviewed-by: Robbie McElrath <rmcelrath@chromium.org>
Reviewed-by: Dibyajyoti Pal <dibyapal@chromium.org>
Reviewed-by: Mike West <mkwst@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1212066}
  • Loading branch information
cmfcmf authored and Chromium LUCI CQ committed Oct 19, 2023
1 parent 55ab4f5 commit e9dc9a5
Show file tree
Hide file tree
Showing 11 changed files with 456 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@
pre {
white-space: pre-wrap;
}

.iwa-update-btn {
margin-left: 4px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ <h3>IWA Updates</h3>
Discover updates of policy-installed IWAs now
</button>
<div id="iwa-update-discovery-message-div"></div>
<div id="iwa-dev-mode-proxy-updates" style="display: none;"></div>
<h4>Dev Mode Proxy App Updates</h4>
<ul id="iwa-dev-mode-proxy-app-list">
</ul>
</div>
<hr>
</div>

Expand Down
63 changes: 63 additions & 0 deletions chrome/browser/resources/web_app_internals/web_app_internals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import './strings.m.js';

import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {getRequiredElement} from 'chrome://resources/js/util_ts.js';
import {Origin} from 'chrome://resources/mojo/url/mojom/origin.mojom-webui.js';
import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';

import {WebAppInternalsHandler} from './web_app_internals.mojom-webui.js';
Expand All @@ -23,6 +24,26 @@ const iwaSelectFileButton =
getRequiredElement('iwa-select-bundle') as HTMLButtonElement;
const iwaSearchForUpdatesButton = getRequiredElement('iwa-search-for-updates');

/**
* Converts a mojo origin into a user-readable string, omitting default ports.
* @param origin Origin to convert
*
* TODO(b/304717391): Extract origin serialization logic from here and use it
* everywhere `url.mojom.Origin` is serialized in JS/TS.
*/
function originToText(origin: Origin): string {
if (origin.host.length === 0) {
return 'null';
}

let result = origin.scheme + '://' + origin.host;
if (!(origin.scheme === 'https' && origin.port === 443) &&
!(origin.scheme === 'http' && origin.port === 80)) {
result += ':' + origin.port;
}
return result;
}

getRequiredElement('copy-button').addEventListener('click', async () => {
navigator.clipboard.writeText(await debugInfoAsJsonString);
});
Expand Down Expand Up @@ -146,6 +167,48 @@ document.addEventListener('DOMContentLoaded', async () => {
if (loadTimeData.getBoolean('experimentalIsIwaDevModeEnabled')) {
// Unhide the IWA install div.
getRequiredElement('iwa-install-div').style.display = '';

const devModeProxyAppList =
getRequiredElement('iwa-dev-mode-proxy-app-list');
const {apps: devModeProxyApps} =
await webAppInternalsHandler.getIsolatedWebAppDevModeProxyAppInfo();
for (const devModeProxyApp of devModeProxyApps) {
const li = document.createElement('li');

li.innerText =
`${devModeProxyApp.name} (${devModeProxyApp.installedVersion}) -> ${
originToText(devModeProxyApp.proxyOrigin)}`;

const updateMsg = document.createElement('p');

const updateBtn = document.createElement('button');
updateBtn.className = 'iwa-update-btn';
updateBtn.innerText = 'Perform update now';
updateBtn.onclick = async () => {
const oldText = updateBtn.innerText;
try {
updateBtn.disabled = true;
updateBtn.innerText =
'Performing update... (close the IWA if it is currently open!)';

const {result} =
await webAppInternalsHandler.updateDevProxyIsolatedWebApp(
devModeProxyApp.appId);
updateMsg.innerText = result;
} finally {
updateBtn.innerText = oldText;
updateBtn.disabled = false;
}
};

li.appendChild(updateBtn);
li.appendChild(updateMsg);

devModeProxyAppList.appendChild(li);
}

// Unhide the div that hides the list of dev mode proxy apps.
getRequiredElement('iwa-dev-mode-proxy-updates').style.display = '';
}
}
});
5 changes: 4 additions & 1 deletion chrome/browser/ui/webui/web_app_internals/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojo_bindings") {
sources = [ "web_app_internals.mojom" ]

public_deps = [ "//url/mojom:url_mojom_gurl" ]
public_deps = [
"//url/mojom:url_mojom_gurl",
"//url/mojom:url_mojom_origin",
]

webui_module_path = "/"
use_typescript_sources = true
Expand Down
13 changes: 13 additions & 0 deletions chrome/browser/ui/webui/web_app_internals/web_app_internals.mojom
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@

module mojom;

import "url/mojom/origin.mojom";
import "url/mojom/url.mojom";

struct InstallIsolatedWebAppResult {
bool success; // true if app was installed successfully.
string error; // should only be set if the app failed to install.
};

struct IwaDevProxyAppInfo {
string app_id;
string name;
url.mojom.Origin proxy_origin;
string installed_version;
};

// Handles requests from chrome://web-app-internals.
// This is expected to be hosted in the browser process.
interface WebAppInternalsHandler {
Expand All @@ -28,4 +36,9 @@ interface WebAppInternalsHandler {
// Triggers update discovery for installed non-dev-mode Isolated Web Apps.
// Returns a string containing a success or error message.
SearchForIsolatedWebAppUpdates() => (string result);
// Returns information about installed dev mode proxy apps.
GetIsolatedWebAppDevModeProxyAppInfo() => (array<IwaDevProxyAppInfo> apps);
// Triggers an update for a dev mode proxy app. Returns a string containing a
// success or error message.
UpdateDevProxyIsolatedWebApp(string app_id) => (string result);
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/webui/web_app_internals/web_app_internals.mojom-forward.h"
#include "chrome/browser/ui/webui/web_app_internals/web_app_internals.mojom.h"
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_dev_mode.h"
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_location.h"
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager.h"
#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
#include "chrome/browser/web_applications/locks/web_app_lock_manager.h"
#include "chrome/browser/web_applications/preinstalled_web_app_manager.h"
#include "chrome/browser/web_applications/web_app.h"
Expand All @@ -33,8 +37,10 @@
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/file_select_listener.h"
#include "content/public/browser/isolated_web_apps_policy.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
#include "third_party/blink/public/mojom/choosers/file_chooser.mojom-shared.h"

#if BUILDFLAG(IS_MAC)
Expand Down Expand Up @@ -618,3 +624,79 @@ void WebAppInternalsHandler::SearchForIsolatedWebAppUpdates(
std::move(callback).Run(base::StringPrintf(
"queued %zu update discovery tasks", queued_task_count));
}

void WebAppInternalsHandler::GetIsolatedWebAppDevModeProxyAppInfo(
GetIsolatedWebAppDevModeProxyAppInfoCallback callback) {
if (!web_app::IsIwaDevModeEnabled(&*profile_)) {
std::move(callback).Run({});
return;
}

auto* provider = web_app::WebAppProvider::GetForWebApps(&profile_.get());
if (!provider) {
std::move(callback).Run({});
return;
}

std::vector<mojom::IwaDevProxyAppInfoPtr> installed_dev_mode_proxy_apps;
for (const web_app::WebApp& app : provider->registrar_unsafe().GetApps()) {
if (!app.isolation_data().has_value()) {
continue;
}
auto* location =
absl::get_if<web_app::DevModeProxy>(&app.isolation_data()->location);
if (location == nullptr) {
continue;
}

installed_dev_mode_proxy_apps.emplace_back(mojom::IwaDevProxyAppInfo::New(
app.app_id(), app.untranslated_name(), location->proxy_url,
app.isolation_data()->version.GetString()));
}

std::move(callback).Run(std::move(installed_dev_mode_proxy_apps));
}

void WebAppInternalsHandler::UpdateDevProxyIsolatedWebApp(
const webapps::AppId& app_id,
UpdateDevProxyIsolatedWebAppCallback callback) {
if (!web_app::IsIwaDevModeEnabled(&*profile_)) {
std::move(callback).Run("IWA dev mode is not enabled");
return;
}

auto* provider = web_app::WebAppProvider::GetForWebApps(&profile_.get());
if (!provider) {
std::move(callback).Run("could not get web app provider");
return;
}

auto* app = provider->registrar_unsafe().GetAppById(app_id);
if (!app || !app->isolation_data().has_value()) {
std::move(callback).Run("could not find installed IWA");
return;
}
if (!absl::holds_alternative<web_app::DevModeProxy>(
app->isolation_data()->location)) {
std::move(callback).Run("can only update dev-mode proxy apps");
return;
}

auto url_info = web_app::IsolatedWebAppUrlInfo::Create(app->manifest_id());
if (!url_info.has_value()) {
std::move(callback).Run("unable to create UrlInfo from start url");
return;
}

auto& manager = provider->iwa_update_manager();
manager.DiscoverApplyAndPrioritizeLocalDevModeUpdate(
app->isolation_data()->location, *url_info,
base::BindOnce([](base::expected<base::Version, std::string> result) {
if (result.has_value()) {
return base::StrCat(
{"Update to version ", result->GetString(),
" successful (refresh this page to reflect the update)."});
}
return "Update failed: " + result.error();
}).Then(std::move(callback)));
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ class WebAppInternalsHandler : public mojom::WebAppInternalsHandler {
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
void SearchForIsolatedWebAppUpdates(
SearchForIsolatedWebAppUpdatesCallback callback) override;
void GetIsolatedWebAppDevModeProxyAppInfo(
GetIsolatedWebAppDevModeProxyAppInfoCallback callback) override;
void UpdateDevProxyIsolatedWebApp(
const webapps::AppId& app_id,
UpdateDevProxyIsolatedWebAppCallback callback) override;

private:
class IsolatedWebAppDevBundleSelectListener;
Expand Down

0 comments on commit e9dc9a5

Please sign in to comment.